[Linux网络编程]04-多进程/多线程并发服务器思路分析及实现(进程,信号,socket,线程...)

一.思路

实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程;
核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程;(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd)

二.多进程并发服务器代码实现:

1.利用while 和 accept 阻塞等待客户端连接,一旦有一个客户端连接上,就创建一个新的子进程执行服务逻辑。

2.子进程结束时,利用信号捕捉函数sigaction回收子进程

3.注意,当主进程阻塞在accept时,若子进程发出信号,accept会被打断(一系列慢速系统调用的特点)从而直接执行接下来的fork函数,这就会导致一个子进程刚好被回收后,又会产生一个新的子进程,为了防止这种情况,使用again和goto维护accept,防止被信号打断后产生错误。

cpp 复制代码
  1 #include<iostream>                                                                                        
  2 #include<unistd.h>
  3 #include<sys/socket.h>
  4 #include<arpa/inet.h>
  5 #include<signal.h>
  6 #include<sys/wait.h>
  7 using namespace std;
  8 
  9 void huidiao(int signo)
 10 {
 11  pid_t pid;
 12  while((pid = waitpid(-1,NULL,WNOHANG))>0)
 13  {
 14    cout<<pid<<endl;
 15  }
 16  return ;
 17 }
 18 
 19 int main()
 20 { 
 21      struct sigaction act;
 22      act.sa_handler = huidiao; sigemptyset(&act.sa_mask);
 24      act.sa_flags = 0;
 25      sigaction(SIGCHLD,&act,NULL);
 26   //socket bind listen accept
 27   int fd = socket(AF_INET,SOCK_STREAM,0);
 28   struct sockaddr_in serveraddr,clientaddr;
 29   serveraddr.sin_family = AF_INET;
 30   serveraddr.sin_port = htons(9876);
 31   serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 32   bind(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
 33 
 34   listen(fd,128);
 35 
 36   socklen_t clientaddr_len = sizeof(clientaddr);
 37   int sfd,flag;
 38   while(true)
 39   {
 40     sfd = accept(fd,(struct sockaddr*)&clientaddr,&clientaddr_len);
 41     flag = fork();
 42    
 43     if(flag == 0)
 44     {                                      close(fd);
 46       break;
 47     }
 48     else if(flag <0)
 49     {
 50       exit(1);
 51     }
 52     else if(flag>0) 
 53     {
 54       close(sfd);
 55       continue;
 56     }
 57   } 
 58 
 59     if(flag == 0)
 60 {                                                                           
 61       for(;;)
 62     {
 63        char buf[1024];
 64        int n = read(sfd,buf,sizeof(buf));
 65        if(n==0)
 66        {                              close(sfd);
 68           cout<<"jieshu"<<endl;
 69           exit(0);
 70        }
 71           for(int i = 0;i<n;i++)
 72           {
 73             buf[i] = toupper(buf[i]);
 74           }
 75           write(STDOUT_FILENO,buf,n);
 76           write(sfd,buf,n);
 77     }
 78 }
 79  return 0;
 80 }

三. 多线程并发服务器代码实现:

cpp 复制代码
1.#include <stdio.h>  
2.#include <string.h>  
3.#include <arpa/inet.h>  
4.#include <pthread.h>  
5.#include <ctype.h>  
6.#include <unistd.h>  
7.#include <fcntl.h>  
8.  
9.#include "wrap.h"  
10.  
11.#define MAXLINE 8192  
12.#define SERV_PORT 8000  
13.  
14.struct s_info {                     //定义一个结构体, 将地址结构跟cfd捆绑  
15.    struct sockaddr_in cliaddr;  
16.    int connfd;  
17.};  
18.  
19.void *do_work(void *arg)  
20.{  
21.    int n,i;  
22.    struct s_info *ts = (struct s_info*)arg;  
23.    char buf[MAXLINE];  
24.    char str[INET_ADDRSTRLEN];      //#define INET_ADDRSTRLEN 16  可用"[+d"查看  
25.  
26.    while (1) {  
27.        n = Read(ts->connfd, buf, MAXLINE);                     //读客户端  
28.        if (n == 0) {  
29.            printf("the client %d closed...\n", ts->connfd);  
30.            break;                                              //跳出循环,关闭cfd  
31.        }  
32.        printf("received from %s at PORT %d\n",  
33.                inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),  
34.                ntohs((*ts).cliaddr.sin_port));                 //打印客户端信息(IP/PORT)  
35.  
36.        for (i = 0; i < n; i++)   
37.            buf[i] = toupper(buf[i]);                           //小写-->大写  
38.  
39.        Write(STDOUT_FILENO, buf, n);                           //写出至屏幕  
40.        Write(ts->connfd, buf, n);                              //回写给客户端  
41.    }  
42.    Close(ts->connfd);  
43.  
44.    return (void *)0;  
45.}  
46.  
47.int main(void)  
48.{  
49.    struct sockaddr_in servaddr, cliaddr;  
50.    socklen_t cliaddr_len;  
51.    int listenfd, connfd;  
52.    pthread_t tid;  
53.  
54.    struct s_info ts[256];      //创建结构体数组.  
55.    int i = 0;  
56.  
57.    listenfd = Socket(AF_INET, SOCK_STREAM, 0);                     //创建一个socket, 得到lfd  
58.  
59.    bzero(&servaddr, sizeof(servaddr));                             //地址结构清零  
60.    servaddr.sin_family = AF_INET;  
61.    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);                               //指定本地任意IP  
62.    servaddr.sin_port = htons(SERV_PORT);                                       //指定端口号   
63.  
64.    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));             //绑定  
65.  
66.    Listen(listenfd, 128);                                                      //设置同一时刻链接服务器上限数  
67.  
68.    printf("Accepting client connect ...\n");  
69.  
70.    while (1) {  
71.        cliaddr_len = sizeof(cliaddr);  
72.        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);   //阻塞监听客户端链接请求  
73.        ts[i].cliaddr = cliaddr;  
74.        ts[i].connfd = connfd;  
75.  
76.        pthread_create(&tid, NULL, do_work, (void*)&ts[i]);  
77.        pthread_detach(tid);                                                    //子线程分离,防止僵线程产生.  
78.        i++;  
79.    }  
80.  
81.    return 0;  
82.}  
相关推荐
律宏阔5 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔5 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴5 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千6 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei376 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei376 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei376 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW6 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
百万蹄蹄向前冲6 小时前
双端同步!云服务器装最新Node.js v26.10全过程追踪
服务器·人工智能·node.js
GeW6 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux