几种并发服务器模型的实现:多线程,多进程,select,poll,epoll

时间:2022-02-26 00:09:52
 

客户端使用select模型:

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <sys/select.h>
10 #include <signal.h>
11
12 #define ERR_EXIT(m) \
13 do { \
14 perror(m);\
15 exit(EXIT_FAILURE);\
16 }while(0)
17
18 static void do_client(int fd)
19 {
20 char recvbuf[MAXLINE + 1] = {0};
21 char sendbuf[MAXLINE + 1] = {0};
22
23 fd_set reade, ready;
24 FD_ZERO(&reade);
25 int fd_stdin = fileno(stdin);
26 FD_SET(fd_stdin, &reade);
27 FD_SET(fd, &reade);
28 int fd_max = (fd_stdin > fd) ? fd_stdin : fd;
29
30 int ret;
31 while(1)
32 {
33 ready = reade;
34 ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询
35 if(ret == -1)
36 {
37 if(errno == EINTR)
38 continue;
39 ERR_EXIT("select");
40 }else if(ret == 0)
41 {
42 continue;
43 }
44
45 if(FD_ISSET(fd_stdin, &ready))
46 {
47 if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
48 {
49 close(fd);
50 break;
51 }else
52 {
53 if( -1 == write(fd, sendbuf, strlen(sendbuf)))
54 printf("write\n");
55 }
56 }
57
58
59 if(FD_ISSET(fd, &ready))
60 {
61 int nread = read(fd, recvbuf, MAXLINE);
62 if(nread < 0)
63 ERR_EXIT("read");
64 if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环
65 {
66 fprintf(stdout, "fd close\n");
67 break;
68 }
69 fprintf(stdout, "receive:%s", recvbuf);
70 }
71 memset(recvbuf, 0, sizeof recvbuf);
72 memset(sendbuf, 0, sizeof sendbuf);
73 }
74 }
75 void handle(int signum)
76 {
77 printf("sigpipe\n");
78 }
79
80 int main(int argc, const char *argv[])
81 {
82 signal(SIGPIPE, SIG_IGN);
83 int fd = socket(AF_INET, SOCK_STREAM, 0);
84 if(fd < 0)
85 ERR_EXIT("socket");
86
87 struct sockaddr_in cliaddr;
88 cliaddr.sin_family = AF_INET;
89 cliaddr.sin_port = htons(8888);
90 cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
91 socklen_t len = sizeof cliaddr;
92
93 int ret ;
94 if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)
95 {
96 close(fd);
97 ERR_EXIT("connect");
98 }
99 do_client(fd);
100 close(fd);
101 return 0;
102 }

 

 

1.并发多进程服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"//封装了网络编程中的readn writen readline 3个函数
#include <signal.h>

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_service(int fd)
{
rio_t rt;
rio_init(&rt, fd);
char recvbuf[1024] = {0};
int ret;
while(1)
{
memset(recvbuf, 0, sizeof recvbuf);
ret = rio_readline(&rt, recvbuf, 1024);
if(ret == 0)
{
close(fd);
exit(EXIT_SUCCESS);
}
rio_writen(fd, recvbuf, strlen(recvbuf)) ;
}
}

void handle(int signum)//对SIGCHLD信号的处理函数
{
while(waitpid(-1, NULL, WNOHANG) > 0);
return ;
}

int listenf();//封装了socket,bind,listen3个函数,返回需要监听的连接socket描述符

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, SIG_IGN) == SIG_ERR)//对客户端关闭导致的信号的处理
ERR_EXIT("signal_pipe");
if(signal(SIGCHLD, handle) == SIG_ERR)//对子进程结束后资源的回收
ERR_EXIT("signal_chld");


int listenfd = listenf();

struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
socklen_t cli_len = sizeof cliaddr;
int clientfd;
while(1)
{
clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
if(clientfd == -1)
{
close(listenfd);
ERR_EXIT("accept");
}

int pid;
if((pid = fork()) == -1)
{
ERR_EXIT("fork");
}else if(pid == 0)
{
close(listenfd);
do_service(clientfd);
exit(EXIT_SUCCESS);
}
close(clientfd);
}
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("socket");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

 2.多线程服务器模型:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rio.h"
#include <signal.h>
#include <pthread.h>

#define ERR_EXIT(m) \
do { \
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

static void do_service(int fd)
{
rio_t rt;
rio_init(&rt, fd);
char recvbuf[1024] = {0};
int ret;
int rcnt = 0;
while(1)
{
memset(recvbuf, 0, sizeof recvbuf);
ret = rio_readline(&rt, recvbuf, 1024);
if(ret == 0)
{
close(fd);
// break;
pthread_exit(NULL);
}
write(fd, recvbuf, strlen(recvbuf)) ;
}
}

void handle(int signum)
{
printf("hello\n");
}

void *pthread_func(void *arg)
{
pthread_detach(pthread_self());
int *q = (int*)arg;
int p = *(int*)arg;
free(q);
do_service(p);
close(p);
}
int listenf();

int main(int argc, const char *argv[])
{
if(signal(SIGPIPE, handle) == SIG_ERR)
ERR_EXIT("signal");
int listenfd = listenf();

struct sockaddr_in cliaddr;
bzero(&cliaddr, sizeof(cliaddr));
socklen_t cli_len = sizeof cliaddr;
int clientfd;
while(1)
{
clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);
if(clientfd == -1)
{
close(listenfd);
ERR_EXIT("accept");
}

pthread_t tid;
int *p = (int*)malloc(sizeof(int));
*p = clientfd;
pthread_create(&tid, NULL, pthread_func, p);
}
close(listenfd);
return 0;
}

int listenf()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
if(listenfd == -1 )
ERR_EXIT("listen");

int on = 1;
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
{
close(listenfd);
ERR_EXIT("setsockopt");
}

struct sockaddr_in seraddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8888);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
socklen_t len = sizeof(seraddr);
if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
{
close(listenfd);
ERR_EXIT("bind");
}

if(listen(listenfd, 6) == -1)
{
close(listenfd);
ERR_EXIT("listen");
}
return listenfd;
}

 3.select服务器模型:

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <signal.h>
11 #include <sys/select.h>
12 #define MAXLEN 1024
13
14 #define ERR_EXIT(m) \
15 do { \
16 perror(m);\
17 exit(EXIT_FAILURE);\
18 }while(0)
19
20 int listenf();//封装了网络socket的socekt,bind,listen函数,返回监听的socket描述符
21 void handle(int signum)//SIGPIPE(子进程结束)的信号的处理
22 {
23 printf("hello\n");
24 }
25 static void do_select(int);
26
27 int main(int argc, const char *argv[])
28 {
29 if(signal(SIGPIPE, handle) == SIG_ERR)
30 ERR_EXIT("signal");
31 int listenfd = listenf();
32 do_select(listenfd);
33 close(listenfd);
34 return 0;
35 }
36
37 int listenf()
38 {
39 int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
40 if(listenfd == -1 )
41 ERR_EXIT("listen");
42
43 int on = 1;
44 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
45 {
46 close(listenfd);
47 ERR_EXIT("setsockopt");
48 }
49
50 struct sockaddr_in seraddr;
51 seraddr.sin_family = AF_INET;
52 seraddr.sin_port = htons(8888);
53 seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
54 socklen_t len = sizeof(seraddr);
55 if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
56 {
57 close(listenfd);
58 ERR_EXIT("bind");
59 }
60
61 if(listen(listenfd, 6) == -1)
62 {
63 close(listenfd);
64 ERR_EXIT("listen");
65 }
66 return listenfd;
67 }
68
69 void do_select(int fd)
70 {
71 //struct sockaddr_in cliaddr;
72 //memset(&cliaddr, 0, sizeof(cliaddr));//此处不需要客户端的地址信息
73 fd_set set;
74 fd_set rset;
75 FD_ZERO(&rset);
76 FD_SET(fd, &rset);
77 int nready;
78 int fd_set[MAXLEN];
79
80 int i;
81 for(i = 0;i < MAXLEN; ++i)
82 fd_set[i] = -1;
83 fd_set[0] = fd;
84
85 int maxi = fd;//初始最大轮询fd是监听的fd
86 int arrlen = 1;//表示数组长度
87
88 char recvbuf[1024] = {0};
89 while(1)
90 {
91 set = rset;
92 nready = select(maxi+1, &set, NULL, NULL, NULL);
93 if(nready == -1)
94 {
95 ERR_EXIT("select");
96 }
97 if(FD_ISSET(fd, &set))//查看书否有新的客户端请求
98 {
99 int clifd = accept(fd, NULL, NULL);
100 if(clifd == -1)
101 ERR_EXIT("accept");
102 for(i = 1; i < MAXLEN; ++i)
103 { if(fd_set[i] == -1)
104 {
105 fd_set[i] = clifd;
106 break;
107 }
108 }
109 FD_SET(clifd, &rset);
110 if(clifd > maxi)
111 maxi = clifd;
112 arrlen++;
113 --nready;
114 }
115
116 for(i = 1; i < arrlen; ++i)//轮询数据连接
117 {
118 int set_fd = fd_set[i];
119 if(FD_ISSET(set_fd, &set))
120 {
121 int n = read(set_fd, recvbuf, 1024);
122 if(n == -1)
123 ERR_EXIT("read");
124 else if(n == 0)//客户端退出,
125 {
126 FD_CLR(set_fd, &rset);
127 close(set_fd);
128 fd_set[i] = -1;
129 arrlen--;
130 }
131 write(set_fd, recvbuf, strlen(recvbuf));
132 memset(recvbuf, 0, 1024);
133 if(--nready < 0)
134 break;
135 }
136 }
137 }
138 }


4

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <signal.h>
11 #include <poll.h>
12 #define MAXLEN 1024
13
14 #define ERR_EXIT(m) \
15 do { \
16 perror(m);\
17 exit(EXIT_FAILURE);\
18 }while(0)
19
20 static void do_service(int);
21 int listenf();
22 void handle(int signum)
23 {
24 printf("hello\n");
25 }
26 static void do_poll(int);
27
28 int main(int argc, const char *argv[])
29 {
30 if(signal(SIGPIPE, handle) == SIG_ERR)
31 ERR_EXIT("signal");
32 int listenfd = listenf();
33 do_poll(listenfd);
34 close(listenfd);
35 return 0;
36 }
37
38 int listenf()
39 {
40 int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
41 if(listenfd == -1 )
42 ERR_EXIT("listen");
43
44 int on = 1;
45 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
46 {
47 close(listenfd);
48 ERR_EXIT("setsockopt");
49 }
50
51 struct sockaddr_in seraddr;
52 seraddr.sin_family = AF_INET;
53 seraddr.sin_port = htons(8888);
54 seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
55 socklen_t len = sizeof(seraddr);
56 if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
57 {
58 close(listenfd);
59 ERR_EXIT("bind");
60 }
61
62 if(listen(listenfd, 6) == -1)
63 {
64 close(listenfd);
65 ERR_EXIT("listen");
66 }
67 return listenfd;
68 }
69
70 void do_poll(int fd)
71 {
72 struct pollfd pfd[MAXLEN];//设置的最大连接数,存放结构体信息
73 char recvbuf[1024] = {0};
74 int i;
75 for(i = 0; i < MAXLEN; ++i)
76 pfd[i].fd = -1;
77
78 pfd[0].fd = fd;
79 pfd[0].events = POLLIN;//将监听连接的描述符防止数组开头
80
81 int nready;
82 int maxi = 0;
83 while(1)
84 {
85 nready = poll(pfd, maxi+1, -1);//轮询的是有效数组长度,不能少1
86 if(nready == -1)
87 {
88 ERR_EXIT("select");
89 }
90
91 if(pfd[0].revents & POLLIN)//有客户端请求连接
92 {
93 int clifd = accept(fd, NULL, NULL);
94 if(clifd == -1)
95 ERR_EXIT("accept");
96 for(i = 1; i < MAXLEN; ++i)
97 {
98 if(pfd[i].fd == -1)
99 {
100 pfd[i].fd = clifd;
101 pfd[i].events = POLLIN;
102 break;
103 }
104 }
105 if(i > maxi)
106 maxi = i;
107 --nready;
108 }
109
110 for(i = 1; i <= maxi; ++i)
111 {
112 if(pfd[i].fd == -1)
113 continue;
114
115 if( pfd[i].revents & POLLIN)
116 {
117 int n = read(pfd[i].fd, recvbuf, 1024);
118 if(n == -1)
119 ERR_EXIT("read");
120 else if(n == 0)//客户端退出,则从集合中清除
121 {
122 printf("%d close\n", pfd[i].fd);
123 close(pfd[i].fd);
124 pfd[i].fd = -1;
125 continue;
126 }
127 write(pfd[i].fd, recvbuf, strlen(recvbuf));
128 memset(recvbuf, 0, 1024);
129
130 if(--nready < 0)
131 break;
132 }
133 }
134 }
135 }

5.epoll服务器模型:

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <signal.h>
11 #include <sys/epoll.h>
12 #define MAXLEN 1024
13
14 #define ERR_EXIT(m) \
15 do { \
16 perror(m);\
17 exit(EXIT_FAILURE);\
18 }while(0)
19
20 int listenf();
21 static void do_epoll(int);
22
23 int main(int argc, const char *argv[])
24 {
25 int listenfd = listenf();
26 do_epoll(listenfd);
27 close(listenfd);
28 return 0;
29 }
30
31 int listenf()
32 {
33 int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd
34 if(listenfd == -1 )
35 ERR_EXIT("listen");
36
37 int on = 1;
38 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用
39 {
40 close(listenfd);
41 ERR_EXIT("setsockopt");
42 }
43
44 struct sockaddr_in seraddr;
45 seraddr.sin_family = AF_INET;
46 seraddr.sin_port = htons(8888);
47 seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
48 socklen_t len = sizeof(seraddr);
49 if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,
50 {
51 close(listenfd);
52 ERR_EXIT("bind");
53 }
54
55 if(listen(listenfd, 6) == -1)
56 {
57 close(listenfd);
58 ERR_EXIT("listen");
59 }
60 return listenfd;
61 }
62
63 void do_epoll(int fd)
64 {
65 char recvbuf[MAXLEN] = {0};
66 int epollfd = epoll_create(2048);//设置的最大连接数
67 if(epollfd == -1)
68 ERR_EXIT("epoll_create");
69
70 struct epoll_event ev;
71 ev.data.fd = fd;
72 ev.events = EPOLLIN;
73 if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)//加入轮询
74 ERR_EXIT("epoll_ctl_add");
75
76 struct epoll_event events[2048];//数组在epoll_wait返回结果的时候使用
77 int ret;
78 int i;//在下面while的for循环中遍历使用
79 int rfd;
80 int clientfd;
81 int nread;
82 while(1)
83 {
84 ret = epoll_wait(epollfd, events, 2048, -1);
85 if(ret == -1)
86 ERR_EXIT("epoll_wait");
87
88 for(i = 0; i < ret; ++i )
89 {
90 rfd = events[i].data.fd;
91 if(rfd == fd)
92 {
93 if((clientfd = accept(fd, NULL, NULL)) == -1)
94 ERR_EXIT("accept");
95 ev.data.fd = clientfd;
96 ev.events = EPOLLIN;
97 if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev) == -1)
98 ERR_EXIT("epoll_ctl");
99 }else
100 {
101 int nread = read(rfd, recvbuf, MAXLEN);
102 if(nread == -1)
103 {
104 if(errno == EINTR)
105 continue;
106 ERR_EXIT("read");
107 }else if( nread == 0)//客户端退出,从epoll轮询中删除
108 {
109 printf("%d fd close\n", rfd);
110 ev.data.fd = rfd;
111 ev.events = EPOLLIN;
112 if(epoll_ctl(epollfd, EPOLL_CTL_DEL, rfd, &ev) == -1)
113 ERR_EXIT("epoll_ctl");
114 }else
115 {
116 if(write(rfd, recvbuf, strlen(recvbuf)) == -1)
117 ERR_EXIT("write");
118 memset(recvbuf, 0, MAXLEN);
119 }
120 }
121 }
122 }
123 close(epollfd);
124 }

 

 

 

.poll服务器模型: