目录
一,往期文章
二,代码实现
关键代码
完整代码
运行效果
一,往期文章
【高并发网络通信架构】1.Linux下实现单客户连接的tcp服务端
二,代码实现
关键代码
- 因为accept是阻塞等待客户端连接,当客户端连接成功后才会执行accept后面的代码,所以为实现多个客户端连接,第一步是将accept放在master循环里。
- recv是阻塞型函数,如果不将recv的相关代码放到线程里去,会导致客户端无法与服务端通信。
完整代码
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #define BUFFER_LENGTH 1024 void* handleClient(void *arg) { int cfd = *((int*)arg); while(1) //一直循环是确保一直和服务端通信,并且recv是阻塞函数 { char buffer[BUFFER_LENGTH] = {0}; int recvLen = recv(cfd,buffer,BUFFER_LENGTH,0); if(recvLen < 0){ printf("recv error code: %d codeInfo: %s\n",errno,strerror(errno)); break; }else if(recvLen == 0){ //客户端断开连接 printf("client fd %d disconnect\n",cfd); close(cfd); //关闭客户端文件描述符,释放资源 break; }else{ printf("recv fd %d data: %s\n",cfd,buffer); send(cfd,buffer,recvLen,0); //将接收到的数据重新发给客户端 } } //退出线程 pthread_exit(NULL); return NULL; } int init_server(int port){ //获取服务端fd,通常为3,前面0,1,2用于指定输入,输出,错误值 int sfd = socket(AF_INET,SOCK_STREAM,0); if(-1 == sfd){ printf("socket error code: %d codeInfo: %s\n",errno,strerror(errno)); return -1; } struct sockaddr_in serverAddr; memset(&serverAddr,0,sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; //ipv4 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); //0.0.0.0 serverAddr.sin_port = htons(port); //绑定IP和端口号 if(-1 == bind(sfd,(struct sockaddr*)&serverAddr,sizeof(struct sockaddr_in))) { printf("bind error code: %d codeInfo: %s\n",errno,strerror(errno)); return -1; } //监听该套接字上的连接 if(-1 == listen(sfd,SOMAXCONN)) { printf("listen error code: %d codeInfo: %s\n",errno,strerror(errno)); return -1; } return sfd; } int main(int argc,char *argv[]){ if(argc < 2)return -1; int port = atoi(argv[1]); int sfd = init_server(port); printf("server fd: %d\n",sfd); if(sfd == -1)exit(0); pthread_t thread_id; struct sockaddr_in clientAddr; socklen_t clientAddrLen = sizeof(struct sockaddr_in); while (1)//每个服务器至少都有一个主循环 { //接受新的连接请求 int cfd = accept(sfd,(struct sockaddr*)&clientAddr,&clientAddrLen); //创建一个新的线程来处理客户端连接 int flag = pthread_create(&thread_id,NULL,handleClient,(void*)&cfd); if(0 == flag){ printf("client fd: %d ,thread ID: %ld start\n",cfd,thread_id); }else{ perror("pthread_create"); break; } //分离线程,让线程自行处理结束 pthread_detach(thread_id); } close(sfd); //关闭服务端文件描述符,释放资源 printf("server fd %d close\n",sfd); return 0; }
运行效果
- 编译时记得加 -lpthread 连接到库,运行如下。
存在问题
- 在程序运行中,不进行客户端连接操作直接按下
Ctrl+Z
会发送一个信号(SIGTSTP)给前台运行的进程,会导致该服务端程序被挂起 (Suspended),导致我下一次在运行时会打印如下错误,说明上次运行并没有释放服务端套接字。(没有客户端连接的情况下)- 因为资源没有释放,导致每次要手动去杀死该挂起的进程,就很麻烦,如下。
- 最后执行 kill -9 394407 杀死挂起的进程。
解决方法文章来源:https://www.toymoban.com/news/detail-533642.html
- 最终的原因是程序如何退出的问题,要使程序直接退出,你可以注册一个信号处理函数来捕获
SIGTSTP
信号,并在该函数中执行退出逻辑。
问题思考文章来源地址https://www.toymoban.com/news/detail-533642.html
- 这种实现方式对于几十几百个客户端并发连接可能没问题,但对于成千上万的客户端进行高并发连接,仍采用这种方式来处理显然是不现实的?下一章介绍。。。
到了这里,关于【高并发网络通信架构】2.引入多线程实现多客户端连接的tcp服务端的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!