1 #include <stdio.h>
2 #include <strings.h> //bzero
3 #include <unistd.h> //close
4 #include <sys/socket.h> //socket
5 #include <netinet/in.h> //struct sockaddr_in
6 #include <arpa/inet.h> //inet_addr
7 #include <string.h>
8 #include <signal.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <stdlib.h>
12
13 void deal_son(int arg)
14 {
15 printf("即将回收子进程资源\n");
16 wait(NULL);
17 printf("已经回收子进程资源\n");
18 }
19
20 int main(int argc, char const *argv[])
21 {
22 // 1.创建套接字
23 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
24 if (sockfd < 0)
25 {
26 perror("socket");
27 return -1;
28 }
29 // 端口复用
30 int yes = 1;
31 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
32 // 2.绑定bind
33 struct sockaddr_in my_addr;
34 my_addr.sin_family = AF_INET;
35 my_addr.sin_port = htons(8000);
36 my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 机器的所有可用IP地址
37 int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
38 if (ret != 0)
39 {
40 perror("bind");
41 return -1;
42 }
43 // 3.监听
44 int backlog = 10;
45 ret = listen(sockfd, backlog);
46 if (ret < 0)
47 {
48 perror("listen");
49 return -1;
50 }
51 // 4.提取,等待一个客户端连接
52 struct sockaddr_in cli_addr;
53 socklen_t len = sizeof(cli_addr);
54 char buf[1024] = "";
55 char ip[16] = "";
56 int sockfd_new = 0;
57 while (1)
58 {
59 // 保证提取到了正确的客户端,而不是被信号打断
60 while (1)
61 {
62 sockfd_new = accept(sockfd, (struct sockaddr *)&cli_addr, &len);
63 if (sockfd_new > 0)
64 break;
65 }
66
67 inet_ntop(AF_INET, (void *)&cli_addr.sin_addr, ip, 16);
68 printf("提取到的客户端: %s--->%hu\n", ip, ntohs(cli_addr.sin_port)); // ip port
69
70 pid_t pid = fork();
71 if (pid < 0)
72 close(sockfd_new);
73 else if (pid == 0)
74 {
75 close(sockfd);
76 // 5.循环收发消息
77 while (1)
78 {
79 bzero(buf, sizeof(buf));
80 recv(sockfd_new, buf, sizeof(buf), 0);
81 printf("接收到%s:%hu--->%s\n", ip, ntohs(cli_addr.sin_port), buf);
82 send(sockfd_new, "ok", sizeof("ok"), 0);
83 if (strcmp(buf, "quit") == 0)
84 break;
85 // 如果收到了0长度的数据包,意思客户端断开连接,子进程结束
86 if (strlen(buf) == 0)
87 break;
88 }
89 _exit(1);
90 }
91 else
92 {
93 close(sockfd_new);
94 // 当子进程结束时,会给父进程发送SIGCHLD信号,父进程收到信号回收资源
95 signal(SIGCHLD, deal_son); // 无阻塞
96 }
97 }
98
99 // 6.关闭套接字
100 close(sockfd_new);
101 close(sockfd);
102 return 0;
103 }
- 创建套接字。
- 设置端口复用。
- 绑定套接字到一个地址和端口。
- 开始监听连接请求。
- 接受连接请求。
- 创建一个新的进程来处理每一个客户端的连接。
- 子进程中处理客户端请求,并发送响应。
- 父进程回收结束的子进程资源,避免僵尸进程。
- 关闭连接和套接字。