linux 多进程实现并发服务器 多线程并发
1.linux socket地址2.linux IP 地址转换3.linux TCP 通信流程 套接字函数 socket() bind() listen() accept() connect()4.linux TCP通信实现 服务器端 - 客户端5.linux TCP 三次握手6.linux TCP滑动窗口 四次挥手
7.linux 多进程实现并发服务器 多线程并发
8.linux TCP状态转换 半关闭 shutdown netstat 端口复用 setsockopt9.linux IO多路复用 select poll epoll10.linux 多路复用---poll11.linux 多路复用---epoll12.linux UDP 通信13.linux 本地套接字通信14.linux Web服务器以及HTTP协议15.linux 服务器编程基本框架和两种高效事件的处理模式16.linux 线程池 EPOLLONESHOT事件TCP通信并发**
要实现TCP通信服务器处理并发任务,使用多线程或多进程来解决。
思路:
1. 一个父进程,多个子进程
2. 父进程负责等待并接受客户端的连接
3. 完成通信,接受一个客户端连接,就创建一个子进程用于通信。
客户端:
1 //TCP通信的客户端 2 3 #include <stdio.h> 4 #include <arpa/inet.h> 5 #include <unistd.h> 6 #include <string.h> 7 #include <stdlib.h> 8 int main() 9 { 10 //1.创建套接字 11 int fd = socket(AF_INET, SOCK_STREAM, 0); 12 if(fd == -1) 13 { 14 perror("socket"); 15 exit(-1); 16 } 17 //2.连接服务器端 18 struct sockaddr_in serveraddr; 19 serveraddr.sin_family = AF_INET; 20 inet_pton(AF_INET, "10.0.8.5", &serveraddr.sin_addr.s_addr);//本机 ip 21 serveraddr.sin_port = htons(9999); 22 int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); 23 if(ret == -1) 24 { 25 perror("connect"); 26 exit(-1); 27 } 28 //3.通信 29 char recvBuf[1024] = {0}; 30 int i = 0; 31 while(1) 32 { 33 sprintf(recvBuf, "data : %d\n",i++); 34 //给服务器端发送数据 35 write(fd, recvBuf, strlen(recvBuf));//要发送\n换行符 应该为 strlen(recvBuf)+1 36 //读取数据 37 int len = read(fd, recvBuf, sizeof(recvBuf)); 38 if(len == -1) 39 { 40 perror("read"); 41 exit(-1); 42 } 43 else if(len > 0) 44 { 45 printf("recv server data: %s\n",recvBuf); 46 } 47 else if(len == 0) 48 { 49 //表示客户端断开连接 50 printf("server closed...\n"); 51 break; 52 } 53 sleep(1); 54 } 55 //关闭连接 56 close(fd); 57 return 0; 58 }
服务器端:
1 #include <stdio.h> 2 #include <arpa/inet.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <signal.h> 7 #include <wait.h> 8 #include <errno.h> 9 void recyleChild(int arg) 10 { 11 while(1) 12 { 13 int ret = waitpid(-1, NULL, WNOHANG); 14 if(ret == -1) 15 { 16 //所有的子进程都回收完了 17 break; 18 } 19 else if(ret == 0) 20 { 21 //还有子进程 22 break; 23 } 24 else if(ret > 0) 25 { 26 //被回收了 27 printf("子进程 %d 被回收了\n",ret); 28 } 29 } 30 } 31 32 int main() 33 { 34 struct sigaction act; 35 act.sa_flags = 0; 36 sigemptyset(&act.sa_mask);//清空掩码 37 act.sa_handler = recyleChild; 38 //注册信号捕捉 39 sigaction(SIGCHLD, &act, NULL); 40 //创建socket 41 int lfd = socket(PF_INET, SOCK_STREAM, 0); 42 if(lfd == -1) 43 { 44 perror("socket"); 45 exit(-1); 46 } 47 struct sockaddr_in saddr; 48 saddr.sin_family = AF_INET; 49 saddr.sin_port = htons(9999); 50 saddr.sin_addr.s_addr = INADDR_ANY; 51 // 绑定 52 int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)); 53 if(ret == -1) 54 { 55 perror("bind"); 56 exit(-1);//exit(0);//0为正常退出 -1 为异常退出 57 } 58 //监听 59 ret = listen(lfd, 128); 60 if(ret == -1) 61 { 62 perror("listen"); 63 exit(-1); 64 } 65 //不断循环等待客户端连接 66 while(1)//并发通信 67 { 68 struct sockaddr_in cliaddr; 69 int len = sizeof(cliaddr); 70 //接受连接 71 int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len); 72 if(cfd == -1) 73 { 74 //终止客户端后,同一客户端要重新连接会软中断,导致无法连接, 且产出accept错误 75 //需要判断是该错误的情况下, 执行下一次循环 76 if(errno == EINTR) 77 { 78 continue;//执行下一次循环 79 } 80 perror("accept"); 81 exit(-1); 82 } 83 //每一个连接进来,创建一个子进程和客户端通信 84 pid_t pid = fork(); 85 if(pid == 0) 86 { 87 //子进程 88 //获取客户端信息 89 char cliIP[16]; 90 inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIP, sizeof(cliIP)); 91 unsigned short cliPort = ntohs(cliaddr.sin_port); 92 printf("client ip is : %s, port is %d\n",cliIP,cliPort); 93 //接受客户端发来的数据 94 char recvBuf[1024] = {0}; 95 while(1) 96 { 97 int len = read(cfd, &recvBuf, sizeof(recvBuf)); 98 if(len == -1) 99 { 100 perror("read"); 101 exit(-1); 102 } 103 else if(len > 0) 104 { 105 printf("recv client : %s\n",recvBuf); 106 } 107 else 108 { 109 printf("client closed...\n"); 110 break; 111 } 112 write(cfd, recvBuf,strlen(recvBuf) + 1);//发送加入换行符 113 } 114 close(cfd); 115 exit(0);//退出当前子进程 116 //退出后要回收子进程资源,wait是阻塞的,不能接受另一个客户端,不能在父进程中进行子进程回收,通过信号回收 117 } 118 } 119 close(lfd);//监听 120 return 0; 121 }
多线程服务器端:
1 #include <stdio.h> 2 #include <arpa/inet.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <pthread.h> 7 //working传入需要三个参数, 但pthread_create只能传入一个参数, 将其封装到一个结构体中使用 8 struct sockInfo 9 { 10 int fd;//通信的文件描述符 11 struct sockaddr_in addr;//客户端信息 12 pthread_t tid;//线程号 13 }; 14 struct sockInfo sockinfos[128]; 15 void * working(void * arg) 16 { 17 //子线程和客户端进行通信 cfd 客户端的信息 线程号 18 //获取客户端信息 19 struct sockInfo * pinfo = (struct sockInfo *)arg; 20 char cliIp[16]; 21 inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, cliIp, sizeof(cliIp)); 22 unsigned short cliPort = ntohs(pinfo->addr.sin_port); 23 printf("client ip is: %s, port is %d\n",cliIp, cliPort); 24 //接收客户端发来的数据 25 char recvBuf[1024]; 26 while(1) 27 { 28 int len = read(pinfo->fd, &recvBuf, sizeof(recvBuf)); 29 if(len == -1) 30 { 31 perror("read"); 32 exit(-1); 33 } 34 else if(len > 0) 35 { 36 printf("recv client : %s\n",recvBuf); 37 } 38 else if(len == 0) 39 { 40 printf("client closed...\n"); 41 break; 42 } 43 write(pinfo->fd, recvBuf, strlen(recvBuf) + 1); 44 } 45 close(pinfo->fd); 46 return NULL; 47 } 48 int main() 49 { 50 //创建socket 51 int lfd = socket(PF_INET, SOCK_STREAM, 0); 52 if(lfd == -1) 53 { 54 perror("socket"); 55 exit(-1); 56 } 57 struct sockaddr_in saddr; 58 saddr.sin_family = AF_INET; 59 saddr.sin_port = htons(9999); 60 saddr.sin_addr.s_addr = INADDR_ANY; 61 // 绑定 62 int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)); 63 if(ret == -1) 64 { 65 perror("bind"); 66 exit(-1);//exit(0);//0为正常退出 -1 为异常退出 67 } 68 //监听 69 ret = listen(lfd, 128); 70 if(ret == -1) 71 { 72 perror("listen"); 73 exit(-1); 74 } 75 //初始化数据 76 int max = sizeof(sockinfos)/sizeof(sockinfos[0]);//128 77 for(int i = 0; i < max; i++) 78 { 79 bzero(&sockinfos[i], sizeof(sockinfos[i])); 80 sockinfos[i].fd = -1;// -1无效文件描述符,表示可用 81 sockinfos[i].tid = -1; 82 } 83 //循环等待客户端连接,一旦一个客户端连接进来, 就创建一个子线程进行通信 84 while(1) 85 { 86 struct sockaddr_in cliaddr; 87 int len = sizeof(cliaddr); 88 //接受连接 89 int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len); 90 struct sockInfo * pinfo; 91 for(int i = 0; i < max; i++) 92 { 93 //从这个数组中找到一个可用的 sockInfo元素 94 if(sockinfos[i].fd == -1) 95 { 96 pinfo = &sockinfos[i]; 97 break; 98 } 99 if(i == max -1 ) 100 { 101 sleep(1); 102 i--; 103 } 104 } 105 pinfo->fd = cfd; 106 memcpy(&pinfo->addr, &cliaddr, len); 107 //创建子线程 108 pthread_create(&pinfo->tid, NULL, working, pinfo);//pinfo.tid 原为 pthread_t pid, &pid 109 pthread_detach(pinfo->tid); 110 } 111 close(lfd); 112 return 0; 113 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了