服务端开发一般流程
一:函数简介
1.1:fork创建子进程
Linux:虚拟地址空间,程序和进程,创建并回收子进程,孤儿进程和僵尸进程。为什么要回收子进程?_来年秋风起^的博客-CSDN博客
这里详细讲述了进程的创建和资源回收,有兴趣的朋友可以去看看。
1.2:socket创建套接字
1.3:setsockopt设置端口复用
1.4:bind绑定端口和IP
其中IP和port为函数传入参数,端口号必须设定一个确定的端口号。
17 int Tcp_socket_bind_listen(const char* IP, unsigned short port)
18 {
19 int lfd = socket(AF_INET, SOCK_STREAM, 0);
20 if(lfd < 0)
21 {
22 perror_exit("socket error");
23 }
24
25 int opt = 1;
26 setsockopt(lfd, SO_REUSEADDR, SOL_SOCKET, &opt, sizeof(opt));
27
28 struct sockaddr_in serv;
29 if(IP){
30 inet_pton(AF_INET, IP, &serv.sin_addr.s_addr);
31 }else {
32 serv.sin_addr.s_addr = htonl(INADDR_ANY);
33 }
34 serv.sin_port = htons(port);
35 int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
36 if(ret < 0)
37 {
38 perror_exit("bind error");
39 }
40
41 listen(lfd, 128); // 设置文件描述符为监听状态
42
43 return lfd;
44 }
二:设置信号处理函数
2.1:SIGCHLD信号
SIGCHLD信号,子进程退出时会给父进程发送该信号, 父进程收到该信号后,不管代码执行到那个位置,都会去执行信号处理函数。我们在该信号处理函数中进行对子进程资源的回收。
这里需要注意的是,一定要在创建子进程之前进行信号处理函数的注册。因为在linux操作系统下,进程的执行顺序是随机的,哪个进程抢到cpu的时间片,哪个进程就先执行。这里让父进程在创建子进程前注册信号处理函数,是为了防止在父进程还未执行到注册信号时子进程就已经执行完代码并退出了,这样就会产生僵尸进程。
三:父子进程分工配合实现多进程服务器
父进程负责进行监听,等待客户端链接,拿到与客户端的通信文件描述符,交给子进程进行数据的交换(通信)。
![]()
这里我只是对客户端发送一个“hello linux”。
最后附上完整代码:
12 void perror_exit(const char* str)
13 {
14 perror(str);
15 exit(-1);
16 }
17
18 int Tcp_socket_bind_listen(unsigned short port)
19 {
20 // 创建socket套接字 指定网络通信,TCP传输协议
21 int lfd = socket(AF_INET, SOCK_STREAM, 0);
22 if(lfd < 0)
23 {
24 perror_exit("socket error");
25 }
26
27 // 设置端口复用
28 int opt = 1;
29 setsockopt(lfd, SO_REUSEADDR, SOL_SOCKET, &opt, sizeof(opt));
30
31 // 绑定lfd与IP和端口
32 struct sockaddr_in serv;
33 serv.sin_addr.s_addr = htonl(INADDR_ANY);
34 serv.sin_port = htons(port);
35 int ret = bind(lfd, (struct sockaddr*)&serv, sizeof(serv));
36 if(ret < 0)
37 {
38 perror_exit("bind error");
39 }
40
41 // 设置文件描述符为监听状态
42 listen(lfd, 128);
43
44 return lfd;
45 }
46
47 int Accept(int fd)
48 {
49 int cfd;
50 do
51 {
52 cfd = accept(fd, NULL, NULL);
53 if(cfd < 0)
54 {
55 if(errno == EINTR)
56 {
57 continue;
58 }
59 return -1;
60 }
61
62 }while(0);
63 return cfd;
64 }
66 void sighandle(int signo)
67 {
68 pid_t pid ;
69 while(1)
70 {
71 pid = waitpid(-1, NULL, WNOHANG);
72 if(pid > 0)
73 {
74 printf("child pid ==[%d] signo == [%d]\n", pid, signo);
75 }
76 else
77 {
78 break;
79 }
80 }
81 }
82
83 int main()
84 {
85 // 创建监听文件描述符
86 int lfd = Tcp_socket_bind_listen(9999);
87
88 // 注册信号捕主捉函数
89 struct sigaction act;
90 act.sa_flags = 0;
91 sigemptyset(&act.sa_mask);
92 act.sa_handler = sighandle;
93 sigaction(SIGCHLD, &act, NULL);
94
95 while(1)
96 {
97 int cfd = Accept(lfd);
98 pid_t pid = fork();
99 if(pid) // 父进程
100 {
101 close(cfd);
102 }
103
104 if(!pid) // 子进程
105 {
106 write(cfd, "hello linux", 11);
107 close(cfd);
108 break;
109 }
110
111 }
112
113 close(lfd);
114
115 return 0;
116 }