linux 多进程实现并发服务器 多线程并发

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 }
复制代码

 

posted on   廿陆  阅读(88)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示