高并发服务器

LinuxC 服务器开发 高并发服务器-多进程并发服务器_#include

一、多进程并发服务器

1. 实现示意图

LinuxC 服务器开发 高并发服务器-多进程并发服务器_#define_02

2. 使用多进程并发服务器时要考虑以下几点:
  • 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)
  • 系统内创建进程个数(与内存大小相关)
  • 进程创建过多是否降低整体服务性能(进程调度)
3. 使用多进程的方式, 解决服务器处理多连接的问题:**

(1)共享

  • 读时共享, 写时复制
  • 文件描述符
  • 内存映射区 – mmap

(2)父进程 的角色是什么?

等待接受客户端连接 – accept

有链接:

  • 创建一个子进程 fork()
  • 将通信的文件描述符关闭

(3)子进程的角色是什么?

1)通信

  • 使用accept返回值 - fd

2)关掉监听的文件描述符

  • 浪费资源

(4)创建的进程的个数有限制吗?

  • 受硬件限制
  • 文件描述符默认也是有上限的1024

(5)子进程资源回收

1)wait/waitpid

2)使用信号回收

  • 信号捕捉

signal
sigaction - 推荐

  • 捕捉信号: SIGCHLD

代码实现:

1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <sys/socket.h>
6
7 void perr_exit(const char *s)
8 {
9 perror(s);
10 exit(-1);
11 }
12
13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
14 {
15 int n;
16
17 again:
18 if ((n = accept(fd, sa, salenptr)) < 0) {
19 if ((errno == ECONNABORTED) || (errno == EINTR))
20 goto again;
21 else
22 perr_exit("accept error");
23 }
24 return n;
25 }
26
27 int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
28 {
29 int n;
30
31 if ((n = bind(fd, sa, salen)) < 0)
32 perr_exit("bind error");
33
34 return n;
35 }
36
37 int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
38 {
39 int n;
40
41 if ((n = connect(fd, sa, salen)) < 0)
42 perr_exit("connect error");
43
44 return n;
45 }
46
47 int Listen(int fd, int backlog)
48 {
49 int n;
50
51 if ((n = listen(fd, backlog)) < 0)
52 perr_exit("listen error");
53
54 return n;
55 }
56
57 int Socket(int family, int type, int protocol)
58 {
59 int n;
60
61 if ((n = socket(family, type, protocol)) < 0)
62 perr_exit("socket error");
63
64 return n;
65 }
66
67 ssize_t Read(int fd, void *ptr, size_t nbytes)
68 {
69 ssize_t n;
70
71 again:
72 if ( (n = read(fd, ptr, nbytes)) == -1) {
73 if (errno == EINTR)
74 goto again;
75 else
76 return -1;
77 }
78 return n;
79 }
80
81 ssize_t Write(int fd, const void *ptr, size_t nbytes)
82 {
83 ssize_t n;
84
85 again:
86 if ( (n = write(fd, ptr, nbytes)) == -1) {
87 if (errno == EINTR)
88 goto again;
89 else
90 return -1;
91 }
92 return n;
93 }
94
95 int Close(int fd)
96 {
97 int n;
98 if ((n = close(fd)) == -1)
99 perr_exit("close error");
100
101 return n;
102 }
103
104 /*参三: 应该读取的字节数*/
105 ssize_t Readn(int fd, void *vptr, size_t n)
106 {
107 size_t nleft; //usigned int 剩余未读取的字节数
108 ssize_t nread; //int 实际读到的字节数
109 char *ptr;
110
111 ptr = vptr;
112 nleft = n;
113
114 while (nleft > 0) {
115 if ((nread = read(fd, ptr, nleft)) < 0) {
116 if (errno == EINTR)
117 nread = 0;
118 else
119 return -1;
120 } else if (nread == 0)
121 break;
122
123 nleft -= nread;
124 ptr += nread;
125 }
126 return n - nleft;
127 }
128
129 ssize_t Writen(int fd, const void *vptr, size_t n)
130 {
131 size_t nleft;
132 ssize_t nwritten;
133 const char *ptr;
134
135 ptr = vptr;
136 nleft = n;
137 while (nleft > 0) {
138 if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
139 if (nwritten < 0 && errno == EINTR)
140 nwritten = 0;
141 else
142 return -1;
143 }
144
145 nleft -= nwritten;
146 ptr += nwritten;
147 }
148 return n;
149 }
150
151 static ssize_t my_read(int fd, char *ptr)
152 {
153 static int read_cnt;
154 static char *read_ptr;
155 static char read_buf[100];
156
157 if (read_cnt <= 0) {
158 again:
159 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
160 if (errno == EINTR)
161 goto again;
162 return -1;
163 } else if (read_cnt == 0)
164 return 0;
165 read_ptr = read_buf;
166 }
167 read_cnt--;
168 *ptr = *read_ptr++;
169
170 return 1;
171 }
172
173 ssize_t Readline(int fd, void *vptr, size_t maxlen)
174 {
175 ssize_t n, rc;
176 char c, *ptr;
177
178 ptr = vptr;
179 for (n = 1; n < maxlen; n++) {
180 if ( (rc = my_read(fd, &c)) == 1) {
181 *ptr++ = c;
182 if (c == '\n')
183 break;
184 } else if (rc == 0) {
185 *ptr = 0;
186 return n - 1;
187 } else
188 return -1;
189 }
190 *ptr = 0;
191
192 return n;
193 }
wrap.c
1 #ifndef __WRAP_H_
2 #define __WRAP_H_
3
4 void perr_exit(const char *s);
5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
8 int Listen(int fd, int backlog);
9 int Socket(int family, int type, int protocol);
10 ssize_t Read(int fd, void *ptr, size_t nbytes);
11 ssize_t Write(int fd, const void *ptr, size_t nbytes);
12 int Close(int fd);
13 ssize_t Readn(int fd, void *vptr, size_t n);
14 ssize_t Writen(int fd, const void *vptr, size_t n);
15 ssize_t my_read(int fd, char *ptr);
16 ssize_t Readline(int fd, void *vptr, size_t maxlen);
17
18 #endif
wrap.h

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 #include "wrap.h"
11
12 #define MAXLINE 8192
13 #define SERV_PORT 8000
14
15 void do_sigchild(int num)
16 {
17 while (waitpid(0, NULL, WNOHANG) > 0);
18 }
19
20 int main(void)
21 {
22 struct sockaddr_in servaddr, cliaddr;
23 socklen_t cliaddr_len;
24 int listenfd, connfd;
25 char buf[MAXLINE];
26 char str[INET_ADDRSTRLEN];
27 int i, n;
28 pid_t pid;
29
30 //临时屏蔽sigchld信号
31 sigset_t myset;
32 sigemptyset(&myset);
33 sigaddset(&myset, SIGCHLD);
34 // 自定义信号集 -》 内核阻塞信号集
35 sigprocmask(SIG_BLOCK, &myset, NULL);
36
37
38 listenfd = Socket(AF_INET, SOCK_STREAM, 0);
39
40 int opt = 1;
41 // 设置端口复用
42 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
43
44 bzero(&servaddr, sizeof(servaddr));
45 servaddr.sin_family = AF_INET;
46 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
47 servaddr.sin_port = htons(SERV_PORT);
48
49 Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
50
51 Listen(listenfd, 20);
52
53 printf("Accepting connections ...\n");
54 while (1)
55 {
56 cliaddr_len = sizeof(cliaddr);
57 connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
58
59 // 有新的连接则创建一个进程
60 pid = fork();
61 if (pid == 0)
62 {
63 Close(listenfd);
64 while (1)
65 {
66 n = Read(connfd, buf, MAXLINE);
67 if (n == 0)
68 {
69 printf("the other side has been closed.\n");
70 break;
71 }
72 printf("received from %s at PORT %d\n",
73 inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
74 ntohs(cliaddr.sin_port));
75
76 for (i = 0; i < n; i++)
77 buf[i] = toupper(buf[i]);
78
79 Write(STDOUT_FILENO, buf, n);
80 Write(connfd, buf, n);
81 }
82 Close(connfd);
83 return 0;
84 }
85 else if (pid > 0)
86 {
87 struct sigaction act;
88 act.sa_flags = 0;
89 act.sa_handler = do_sigchild;
90 sigemptyset(&act.sa_mask);
91 sigaction(SIGCHLD, &act, NULL);
92 // 解除对sigchld信号的屏蔽
93 sigprocmask(SIG_UNBLOCK, &myset, NULL);
94
95 Close(connfd);
96 }
97 else
98 {
99 perr_exit("fork");
100 }
101 }
102 return 0;
103 }
server.c

1 /* client.c */
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7
8 #include "wrap.h"
9
10 #define MAXLINE 8192
11 #define SERV_PORT 8000
12
13 int main(int argc, char *argv[])
14 {
15 struct sockaddr_in servaddr;
16 char buf[MAXLINE];
17 int sockfd, n;
18
19 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
20
21 bzero(&servaddr, sizeof(servaddr));
22 servaddr.sin_family = AF_INET;
23 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
24 servaddr.sin_port = htons(SERV_PORT);
25
26 Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
27
28 while (fgets(buf, MAXLINE, stdin) != NULL)
29 {
30 Write(sockfd, buf, strlen(buf));
31 n = Read(sockfd, buf, MAXLINE);
32 if (n == 0)
33 {
34 printf("the other side has been closed.\n");
35 break;
36 }
37 else
38 Write(STDOUT_FILENO, buf, n);
39 }
40
41 Close(sockfd);
42
43 return 0;
44 }
client.c
1 src = $(wildcard *.c)
2 obj = $(patsubst %.c, %.o, $(src))
3
4 all: server client
5
6 server: server.o wrap.o
7 gcc server.o wrap.o -o server -Wall
8 client: client.o wrap.o
9 gcc client.o wrap.o -o client -Wall
10
11 %.o:%.c
12 gcc -c $< -Wall
13
14 .PHONY: clean all
15 clean:
16 -rm -rf server client $(obj)
makefile

LinuxC/C++服务器开发/架构师面试题、学习资料、教学视频和学习路线图 有需要的可以自行添加学习交流群​960994558​ 或者 资料获取