1.只能处理但链接
监听 绑定 监听 接受连接请求 通信
只有一个进程,则就看成父进程
父进程连接后,可以fork 个子进程去通信,就可以实现一个多进程通信
多进程服务器的实现基础
1.共享: 读时共享,写时复制
文件描述符
内存映射区
父进程的变量,fork 后再子进程中也存在
两者的虚拟空间不同,但其对应的物理空间是同一个。即父子进程在逻辑上仍然是严格相互独立的两个进程,各自维护各自的参数,只是在物理上实现了读时共享,写时复制。
进程中的变量都在虚拟内存里面
内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟空间结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。
2.父进程的角色是什么?
等待接受客户端连接 --accept
有链接:创建一个子进程fork()
将通信的文件描述符关闭(子进程已经在干通信的事情了)
3.子进程的角色是什么?
通信
使用accept 返回值 - fd
关掉监听的文件描述符(减少浪费资源)
4.创建的进程的个数有限制吗?
受硬件限制
文件描述符默认有上限1024
5.子进程资源回收
wait /waitpid
使用信号回收,,signal sigaction - 推荐
捕捉信号:SIGCHLD
pid_t waitpid(pid_t pid,int *status,int options)
一个小型服务器
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/stat.h>
6 #include<string.h>
7 #include<arpa/inet.h>
8 #include<ctype.h>
9 #include<signal.h>
10 #include<sys/wait.h>
11 #include<errno.h>
12 //进程回收函数
13 void recyle(int num)
14 {
15 pid_t pid;
16 while((pid = waitpid(-1,NULL,WNOHANG))>0)
17 {
18 printf("child died,pid = %d\n ",pid);
19 }
20 }
21 int main(int argc,const char* argv[])
22 {
23 if(argc<2)
24 {
25 printf("eg: ./a.out port \n");
26 exit(1);
27
28 }
29 struct sockaddr_in serv_addr;
30 socklen_t serv_len = sizeof(serv_addr);
31 int port = atoi(argv[1]);
32 //创建套接字
33 int lfd = socket(AF_INET,SOCK_STREAM,0);
34 //初始化服务器 sockaddr_in
35 memset(&serv_addr,0,serv_len);
36 serv_addr.sin_family = AF_INET;
37 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
38 serv_addr.sin_port = htons(port);
39 //绑定IP和端口
40 bind(lfd,(struct sockaddr*)&serv_addr,serv_len);
41 //设置同时监听的最大个数
42 listen(lfd,36);
43 printf("start accept....\n");
44 //使用信号来回收子进程的pcd
45 struct sigaction act;
46 act.sa_handler = recyle;//回调函数
47 act.sa_flags = 0;
48 sigemptyset(&act.sa_mask);
49 sigaction(SIGCHLD,&act,NULL);
50 struct sockaddr_in client_addr;
51 socklen_t cli_len = sizeof(client_addr);
52 while(1)
53 {
54 //父进程接收连接请求
55 accept阻塞的时候被信号中断(子进程被处理),处理对应的的操作之后,回来之后不阻塞了,直接返回-1,这时候error == EINTR
56 int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
57 while(cfd == -1 && errno == EINTR)
58 {
59 cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
60 }
61 if(cfd == -1)
62 {
63 perror("accept error");
64 exit(1);
65 }
66 printf("connect sucessful\n");
67 //创建子进程
68 pid_t pid = fork();
69 if(pid == 0)
70 {
71 //child process
72 char ip[64];
73 while(1)
74 {
75 //client ip port
76 printf("client IP:%s,port:%d\n",inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)),ntohs(client_add r.sin_port));
77 char buf[1024];
78 int len = read(cfd,buf,sizeof(buf));
79 if(len == -1)
80 {
81 perror("read error");
82 exit(1);
83 }
84 else if(len==0)
85 {
86 printf("客户端断开连接\n");
87 close(cfd);
88 break;
89 }
90 else
91 {
92 printf("recv buf:%s\n",buf);
93 write(cfd,buf,len);
94 }
95 }
96 //干掉子进程
97 return 0;
98 }
99 else if(pid>0)
100 {
101 //parent process
102 close(cfd); //通信的事情子进程去干了,就可以关掉一个文件描述符了
103
104 }
105 }
106 close(lfd);
107 return 0;
108 }