I/O复用的 select poll和epoll的简单实现
来源:互联网 发布:淘宝pu女包 编辑:程序博客网 时间:2024/06/02 04:15
http://www.cnblogs.com/wj9012/p/3876734.html
一个tcp的客户端服务器程序
服务器端不变,客户端通过I/O复用轮询键盘输入与socket输入(接收客户端的信息)
服务器端:
1 /*服务器: 2 1.客户端关闭后,服务器再向客户端发送信息,第一次会收到一个RST复位报文,第二次会收到SIGPIPE信号,导致服务器关闭,必须对这个信号进行处理: 3 1.在服务器对read返回值为0的情况进行处理,不向客户端发送信息 4 2.signal函数: signal(SIGPIPE, handle) 或者直接忽略signal(SIGPIPE, SIG_IGN) 5 */ 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h>10 #include <errno.h>11 #include <sys/types.h>12 #include <sys/stat.h>13 #include <sys/socket.h>14 #include <arpa/inet.h>15 #include <signal.h>16 17 #define ERR_EXIT(m) \18 do { \19 perror(m);\20 exit(EXIT_FAILURE);\21 }while(0)//宏定义的错误处理22 23 static void do_service(int fd)24 {25 char recvbuf[1024] = {0};26 int ret;27 while(1)28 {29 memset(recvbuf, 0, sizeof recvbuf);30 ret = read(&rt, recvbuf, 1024);31 sleep(3);32 if(ret == 0)//关闭客户端时会导致ret == 033 printf("no message\n");34 else if(ret == -1)35 {36 if(errno == EINTR)37 continue;38 return ;39 } 40 rio_writen(fd, recvbuf, 5);41 }42 }43 44 void handle(int signum)//SIGPIPE信号处理函数45 {46 printf("hello\n");47 }48 49 int main(int argc, const char *argv[])50 {51 if(signal(SIGPIPE, handle) == SIG_ERR)52 ERR_EXIT("signal"); 53 54 int listenfd = socket(AF_INET, SOCK_STREAM, 0);//准备一个socketfd55 if(listenfd == -1 )56 ERR_EXIT("listen");57 58 int on = 1;59 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)//setsockopt设置端口复用60 {61 close(listenfd);62 ERR_EXIT("setsockopt");63 }64 65 struct sockaddr_in seraddr;66 seraddr.sin_family = AF_INET;67 seraddr.sin_port = htons(8888);68 seraddr.sin_addr.s_addr = htonl(INADDR_ANY);69 socklen_t len = sizeof(seraddr);70 if(bind(listenfd, (struct sockaddr*)&seraddr, len) == -1)//监听socket端口,71 {72 close(listenfd);73 ERR_EXIT("bind");74 }75 //上述过程可以封装到起来,不用全部写入main76 if(listen(listenfd, 6) == -1)77 {78 close(listenfd);79 ERR_EXIT("listen");80 }81 82 struct sockaddr_in cliaddr;83 bzero(&cliaddr, sizeof(cliaddr));84 socklen_t cli_len = sizeof cliaddr;85 86 int clientfd;87 clientfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cli_len);88 89 if(clientfd == -1) 90 {91 close(listenfd);92 ERR_EXIT("accept");93 }94 do_service(clientfd);95 96 close(clientfd);97 close(listenfd);98 return 0;99 }
1.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 11 #define ERR_EXIT(m) \12 do { \13 perror(m);\14 exit(EXIT_FAILURE);\15 }while(0)16 17 static void do_client(int fd)18 {19 char recvbuf[MAXLINE + 1] = {0};20 char sendbuf[MAXLINE + 1] = {0};21 22 fd_set reade, ready;//将ready集合放入select轮询,每次轮询前将reade赋值给ready23 FD_ZERO(&reade);//清空reade集合24 int fd_stdin = fileno(stdin);25 FD_SET(fd_stdin, &reade);26 FD_SET(fd, &reade);//将需要监听的文件描述符加入集合27 int fd_max = (fd_stdin > fd) ? fd_stdin : fd;//select轮询的最大文件描述符28 29 int ret;30 while(1)31 {32 ready = reade;33 ret = select( fd_max+1, &ready, NULL, NULL, NULL);//轮询,最后一个参数struct *timeval设置为NULL表示即时轮询(每次轮询间没时间间隔)34 if(ret < 0)35 {36 if(errno == EINTR)37 continue;38 ERR_EXIT("select");39 }else if(ret == 0)//监听的文件描述符状态未发生变化40 {41 continue;42 }43 44 if(FD_ISSET(fd_stdin, &ready))45 {46 if(fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)47 {48 shutdown(fd, SHUT_WR);//关闭fd的写端,close关闭时关闭整个描述符,若再写的话会导致write返回-1,系统会向客户端进程发送一个SIGPIPE信号,
导致关闭,可以对信号进行signal(SIGPIPE, SIG_IGN)处理,也可将fd_stdin从监听队列删除49 // close(fd);50 exit(EXIT_SUCCESS);51 //break;
52 }else53 write(fd, sendbuf, strlen(sendbuf)); 54 }55 56 if(FD_ISSET(fd, &ready))57 {58 int nread = read(fd, recvbuf, MAXLINE);59 if(nread < 0)60 ERR_EXIT("read");61 if(nread == 0)//如果没接收到消息,打印关闭描述符,退出循环62 {63 fprintf(stdout, "fd close\n");64 break;65 }66 fprintf(stdout, "receive:%s\n", recvbuf);//注意要刷新67 }68 memset(recvbuf, 0, sizeof recvbuf);69 memset(sendbuf, 0, sizeof sendbuf);70 }71 }72 73 int main(int argc, const char *argv[])74 {75 int fd = socket(AF_INET, SOCK_STREAM, 0);76 if(fd < 0)77 ERR_EXIT("socket");78 79 struct sockaddr_in cliaddr;80 cliaddr.sin_family = AF_INET;81 cliaddr.sin_port = htons(8888);82 cliaddr.sin_addr.s_addr = inet_addr("127.0.0.1");83 socklen_t len = sizeof cliaddr;84 85 int ret ;86 if((ret = connect(fd, (struct sockaddr*)&cliaddr, len)) == -1)87 {88 close(fd);89 ERR_EXIT("connect");90 }91 do_client(fd);92 close(fd);93 printf("123\n");94 return 0;95 }
2.poll客户端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
static
void
do_client(
int
fd)
{
char
recvbuf[MAXLINE + 1] = {0};
char
sendbuf[MAXLINE + 1] = {0};
struct
pollfd pfd[2];
//struct pollfd数组,里面存放监听描述符相关信息,供poll轮询使用
pfd[0].fd = fileno(stdin);
pfd[0].events = POLLIN;
pfd[1].fd = fd;
pfd[1].events = POLLIN;
int
ret;
while
(1)
{
ret = poll( pfd, 2, -1);
//轮询,-1和select中的时间结构体指针为NULL的效果一样
if
(ret < 0)
{
if
(
errno
== EINTR)
continue
;
ERR_EXIT(
"select"
);
}
else
if
(ret == 0)
{
continue
;
}
if
(pfd[0].revents & POLLIN)
{
if
(
fgets
(sendbuf,
sizeof
(sendbuf), stdin) == NULL)
{
shutdown(fd, SHUT_WR);
//关闭fd的写端
pfd[0].fd = -1;
}
else
write(fd, sendbuf,
strlen
(sendbuf));
}
if
(pfd[1].revents & POLLIN)
{
int
nread = read(fd, recvbuf, MAXLINE);
if
(nread < 0)
ERR_EXIT(
"read"
);
if
(nread == 0)
//如果没接收到消息,打印关闭描述符,退出循环
{
fprintf
(stdout,
"fd close\n"
);
break
;
}
fprintf
(stdout,
"receive:%s\n"
, recvbuf);
//注意要刷新
}
memset
(recvbuf, 0,
sizeof
recvbuf);
memset
(sendbuf, 0,
sizeof
sendbuf);
}
}
//其余部分和select中一样
3.epoll客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static
void
do_client(
int
fd)
{
char
recvbuf[MAXLINE + 1] = {0};
char
sendbuf[MAXLINE + 1] = {0};
int
epollfd = epoll_create(2);
//返回值也是一个文件描述符,记得要关闭,不然会导致fd耗尽
if
(epollfd == -1)
ERR_EXIT(
"epoll_create"
);
struct
epoll_event events[2];
struct
epoll_event ev;
int
ret;
ev.data.fd = fd;
ev.events = EPOLLIN;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
//注册fd
if
(ret == -1)
ERR_EXIT(
"epoll_ctl"
);
ev.data.fd = fileno(stdin);
ev.events = EPOLLIN;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
//注册fd
if
(ret == -1)
ERR_EXIT(
"epoll_ctl"
);
int
nready;
while
(1)
{
nready = epoll_wait(epollfd, events, 2, -1);
if
(nready < 0)
{
if
(
errno
== EINTR)
continue
;
ERR_EXIT(
"select"
);
}
else
if
(nready == 0)
{
continue
;
}
int
i;
for
(i = 0; i < nready; i++)
{
int
nfd = events[i].data.fd;
if
(nfd == STDIN_FILENO)
{
if
(
fgets
(sendbuf, 1024, stdin) == NULL)
{
shutdown(fd, SHUT_WR);
struct
epoll_event ee;
ee.data.fd = STDIN_FILENO;
if
(epoll_ctl(epollfd, EPOLL_CTL_DEL, STDIN_FILENO, &ee) == -1)
ERR_EXIT(
"epoll_ctl"
);
}
else
write(fd, sendbuf,
strlen
(sendbuf));
}
if
(nfd == fd)
{
int
ret = read(fd, recvbuf, 1024);
if
(ret == -1)
ERR_EXIT(
"readline"
);
else
if
(ret == -1)
{
close(fd);
printf
(
"fd close\n"
);
exit
(EXIT_SUCCESS);
}
printf
(
"recv data :%s\n"
, recvbuf);
}
}
}
memset
(recvbuf, 0,
sizeof
recvbuf);
memset
(sendbuf, 0,
sizeof
sendbuf);<br> }<br>close(epollfd);
}
阅读全文