您的位置 首页 > 数码极客

tcpserver如何和多个client通信

最近看了一些零声学院的linux视频,把“多进程进行socket编程”好好理解了一下,整理出来的。

用TCP协议编写了一个简单的服务器、客户端,其中服务器一直在监听本机8000号端口。如果收到客户端的链接,就在服务器端把客户端的IP和端口号打印出来,收到客户端发送的数据,服务器会把数据变成大写并发送回客户端。要实现多个客户端连接到服务器,就需要解决阻塞问题,比如当服务器在read阻塞读客户端数据时,如果客户端没有数据到达,服务器端就会阻塞在read函数上,这时如果有新的客户端连接请求,由于服务器阻塞在read函数,就不能及时响应客户端的请求,使用多进程并发可以解决这个问题,实现多个客户端连接同一个服务器,当服务器接收到一个客户端的连接后,就fork出一个的进程去处理客户端数据,让父进程去accept接收新的客户端连接请求。代码及详细解释如下:

服务器端程序:

#include <sy; #include <sy; #include <arpa; #include <; #include <; #include <; #include <sy; #include <netine; #include <uni; #include <c; #define SERVER_PORT 8000 //监听本机8000端口 #define MAX 4096 int main(void) { struct sockaddr_in serveraddr,clientaddr; int sockfd,addrlen,confd,len; char ipstr[128]; char buf[4096]; pid_t pid; sockfd = socket(AF_INET,SOCK_STREAM,0); bzero(&serveraddr,sizeof(serveraddr)); //地址族协议ipv4 = AF_INET; //ip地址 = htonl(INADDR_ANY); = htons(SERVER_PORT); bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); listen(sockfd,128); while(1){ //4. accept阻塞监听客户端的链接请求 addrlen = sizeof(clientaddr); confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen); //如果有客户端连接上服务器,就输出客户端的ip地址和端口号 printf("client ip %s\tport %d\n", inet_ntop(AF_INET,(struct sockaddr *)&clien(ipstr)),nto)); //这块是多进程的关键,当accept收到了客户端的连接之后,就创建子进程,让子进程去处理客户端 //发来的数据,父进程里面关闭confd(因为用不到了),然后父进程回到while循环继续监听客户端的连接 pid = fork(); //5. 子进程处理客户端请求 if(pid == 0){//子进程 close(sockfd); while(1){//循环读取客户端发来的数据,把小写变成大写 len = read(confd,buf,sizeof(buf)); int i = 0; while(i < len){ buf[i] = toupper(buf[i]); i++; } write(confd,buf,len); } close(confd); return 0; } else if(pid > 0){//父进程关闭文件描述符,释放资源 close(confd); } } return 0; }

客户端程序:client.c

#include <netine; #include <; #include <sy; #include <sy; #include <; #include <arpa; #include <uni; #include <sy; #include <c; #include <; #define SERVER_PORT 8000 #define MAXLINE 4096 int main(void) { struct sockaddr_in serveraddr; int confd,len; char ipstr[] = "10.170.20.238";//这是服务器的地址,使用ifconfig来查看 char buf[MAXLINE]; //1.创建一个socket confd = socket(AF_INET,SOCK_STREAM,0); //2.初始化服务器地址,指明我要连接哪个服务器 bzero(&serveraddr,sizeof(serveraddr)); = AF_INET; inet_pton(AF_INET,ipstr,&); = htons(SERVER_PORT); //3.链接服务器 connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); while(fgets(buf,sizeof(buf),stdin)){ //4.请求服务器处理数据 write(confd,buf,strlen(buf)); len = read(confd,buf,sizeof(buf)); write(STDOUT_FILENO,buf,len); } //5.关闭socket close(confd); return 0; }

我们在虚拟机下,使用一个虚拟机开启多个终端来观察效果,如果你的虚拟机联网了,就使用ifconfig命令来查看IP地址,填入客户端代码的 ipstr 数组中,这里我的ip是"10.170.20.238",也可以不联网,使用sudo ifconfig ens33 10.170.20.238 自己给虚拟机设置一个虚拟IP(关机之后这个ip就失效了)

注意:sudo ifconfig ens33 192.168.1.12 中的 ens33 每个人的虚拟机不一定一样,还是使用ifconfig查看,如下图:

设置好IP以后,编译和client.c文件,然后开多个终端执行,执行结果如下,开了一个服务器,三个客户端,服务器能正确处理三个客户端的请求。(客户端的端口号是随机的)

上图是开了三个客户端的,那服务器到底能连接多少个客户端呢?这取决于服务器端机器的内存和性能

这个程序存在两个问题,一个是出错处理,为了理解方便我就没加,还有一个就是子进程回收问题,子进程回收我们一般可以使用wait或waitpid让父进程去回收子进程资源,但是服务器端的父进程在等待客户端的连接请求,没办法去回收,还可以使用信号去回收子进程,子进程退出时会给父进程发送SIGCHLD信号,我们可以在信号处理函数里面去回收子进程,但是,执行信号处理函数,会打断父进程的accept,这样就又没法及时响应客户端的连接了,所以我们还是使用waitpid,多创建一个进程通过指定waitpid的第一个参数为-1(回收指定进程组内的任意子进程),专门用来回收子进程

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“tcpserver如何和多个client通信”边界阅读