IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Socket-网络编程-C语言版回声服务器 -> 正文阅读

[C++知识库]Socket-网络编程-C语言版回声服务器

1.回声服务器代码实现

1.server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>


#define SERVER_PORT 666

int main(void){

    int sock;
    struct sockaddr_in server_addr;  //创建保存IP地址于端口号的结构体

    sock = socket(AF_INET, SOCK_STREAM, 0);  //创建服务器端套接字

    bzero(&server_addr, sizeof(server_addr));  //将之前创建的结构体内存清空,防止未知的错误 

    server_addr.sin_family = AF_INET;   //选择协议族IPV4
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //监听本地所有IP地址
    server_addr.sin_port = htons(SERVER_PORT);   //绑定端口号

    bind(sock, (struct sockaddr *)&server_addr,  sizeof(server_addr));    //服务器端绑定IP地址于端口号

    listen(sock, 128);   //监听客户端请求

    printf("等待客户端的连接\n");

    int done =1;
    while(done){
    
        struct sockaddr_in client;
        int client_sock, len, i;
        char client_ip[64];
        char buf[256];

        socklen_t  client_addr_len;
        client_addr_len = sizeof(client);
        client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);   //接收客户端请求,获取客户端套接字

        //打印客服端IP地址和端口号
        printf("client ip: %s\t port : %d\n",
                 inet_ntop(AF_INET, &client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
                 ntohs(client.sin_port));
                 
        /*读取客户端发送的数据*/
        len = read(client_sock, buf, sizeof(buf)-1);
        buf[len] = '\0';
        printf("receive[%d]: %s\n", len, buf);

        //转换成大写
        for(i=0; i<len; i++){
            buf[i] = toupper(buf[i]);
        }
        
        len = write(client_sock, buf, len);

        printf("finished. len: %d\n", len);
        close(client_sock);

}
    close(sock);
    
    return 0;
}

2.client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SERVER_PORT 666
#define SERVER_IP  "127.0.0.1"

int main(int argc, char *argv[]){

    int sockfd;
    char *message;
    struct sockaddr_in servaddr;
    int n;
    char buf[64];

    if(argc != 2){
        fputs("Usage: ./echo_client message \n", stderr);
        exit(1);
    }

    message = argv[1];

    printf("message: %s\n", message);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);   //创建客户端套接字

    memset(&servaddr, '\0', sizeof(struct sockaddr_in));    //清空结构体内存

    servaddr.sin_family = AF_INET;    //设置为ipv4协议
    inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);
    servaddr.sin_port = htons(SERVER_PORT);

    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));    //向服务器发起连接请求

    write(sockfd, message, strlen(message));

    n = read(sockfd, buf, sizeof(buf)-1);

    if(n>0){
        buf[n]='\0';
        printf("receive: %s\n", buf);
    }else {
        perror("error!!!");
    }

    printf("finished.\n");
    close(sockfd);

    return 0;
}

注:客户端运行时要带上要传输的信息。如图:

在这里插入图片描述

2.函数解析

1.socket

int socket(int domain, int type, int protocol);
/*
domain:
	AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
	AF_INET6 与上面类似,不过是来用IPv6的地址
	AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用

type:
	SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
	SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。

protocol:
	传0 表示使用默认协议。

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。

	*/

2.bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
sockfd:
	socket文件描述符
addr:
	构造出IP地址加端口号
addrlen:
	sizeof(addr)长度
*/

3.listen

int listen(int sockfd, int backlog);
/*
sockfd:
	socket文件描述符
backlog:
	在Linux 系统中,它是指排队等待建立3次握手队列长度
*/

4. accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
sockdf:
	socket文件描述符
addr:
	传出参数,返回链接客户端地址信息,含IP地址和端口号
addrlen:
	传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小
	*/

5.connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
ockdf:
	socket文件描述符
addr:
	传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:
	传入参数,传入sizeof(addr)大小
	*/

3.原理讲解

1.Socket 通信流程图

请添加图片描述
首先网络上两台主机要通信,肯定要有标识分别标记两台主机,这样它们才能找到对方。我们知道IP地址可以唯一标识一台主机,但在网络编程中通信的其实是两个进程,所以为了标识不同主机的不同进程我们使用IP地址加端口号的标识方法。

这在服务器端的代码表现在bind函数上。
bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) 其中server_addr是一个结构体,其中的成员保存了IP地址与端口号。这个函数将服务器端套接字和addr绑定在一起,这样客户端就能通过该IP地址与端口号找到服务器。(sockaddr与server_addr的区别:首先,它们都是结构体,但前者是IPV4使用的,后者是IPV6使用的。由于主机越来越多,IPV4可用的地址越来越少,于是有了IPV6。IPV6解决了IPV4地址不够用的问题,理论上未来是会取代IPV4的,但如今IPV4还是在被使用。Linux底层使用server_addr储存IP地址与端口号,但如果要使用IPV4,也就是socket函数输入的参数是AF_INET ,则要进行强制类型转换。总之是历史遗留问题。

看到这可能会感到奇怪,为什么服务器端需要绑定IP地址与端口号,但客户端却没有相应的代码实现。难道客户端不需要吗?其实不然,只不过客户端绑定的这一步骤不需要我们用代码实现,计算机自己会实现。

完成了计算机的标记,我们就能通过IP地址与端口号的组合找到对方了。对于服务器端来说完成标记后,就需要监听(这个监听可以理解为服务器端不时的查看是否有连接,这个时间间隔应该是很短的)服务器端套接字,服务器端套接字与之前设置的IP地址与端口号是绑定的。这在代码里表现在 listen(sock, 128)

之后服务器端执行到 accept(sock, (struct sockaddr *)&client, &client_addr_len) 函数时会阻塞在这里。服务器端的任务就暂时到这了,接下来只等客户端发来连接请求。

在客户端完成绑定后,通过 connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) 函数向服务器端发起连接请求。如果两端使用的是TCP协议,这其中还会有一个三次握手的过程。如下图:

在这里插入图片描述
客户端一开始发送一个请求如图1,服务器端收到后会回复一个报文如图2,客户端收到2后会会回复3表示已收到2。服务器端收到3后三次握手完成,连接建立。服务器端通过accept函数获得客户端套接字,接下来就可以进行通信了。

2.总结

客户端与服务器端各自创建套接字,服务器端绑定IP地址与端口号,监听客户端连接请求,通过accept函数接收客户端信息(储存在作为参数传入accept的结构体中,包括客户端IP地址与端口号)与套接字(返回值)。

客户端通过connect函数连接服务器(服务器IP地址与端口号储存在作为参数传入connect的结构体中),客户端IP地址与端口号的绑定由计算机自己实现。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-31 15:16:03  更:2021-08-31 15:17:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:59:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码