I/O复用的 select poll和epoll的简单实现
生活随笔
收集整理的這篇文章主要介紹了
I/O复用的 select poll和epoll的简单实现
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
http://www.cnblogs.com/wj9012/p/3876734.html
一個tcp的客戶端服務器程序
服務器端不變,客戶端通過I/O復用輪詢鍵盤輸入與socket輸入(接收客戶端的信息)
服務器端:
1 /*服務器: 2 1.客戶端關閉后,服務器再向客戶端發(fā)送信息,第一次會收到一個RST復位報文,第二次會收到SIGPIPE信號,導致服務器關閉,必須對這個信號進行處理: 3 1.在服務器對read返回值為0的情況進行處理,不向客戶端發(fā)送信息 4 2.signal函數(shù): 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?== 0 33 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信號處理函數(shù) 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);//準備一個socketfd 55 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)//監(jiān)聽socket端口, 71 { 72 close(listenfd); 73 ERR_EXIT("bind"); 74 } 75?//上述過程可以封裝到起來,不用全部寫入main 76 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賦值給ready 23 FD_ZERO(&reade);//清空reade集合 24 int fd_stdin = fileno(stdin); 25 FD_SET(fd_stdin, &reade); 26 FD_SET(fd, &reade);//將需要監(jiān)聽的文件描述符加入集合 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);//輪詢,最后一個參數(shù)struct *timeval設置為NULL表示即時輪詢(每次輪詢間沒時間間隔) 34 if(ret < 0) 35 { 36 if(errno == EINTR) 37 continue; 38 ERR_EXIT("select"); 39 }else if(ret == 0)//監(jiān)聽的文件描述符狀態(tài)未發(fā)生變化 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,系統(tǒng)會向客戶端進程發(fā)送一個SIGPIPE信號,導致關閉,可以對信號進行signal(SIGPIPE, SIG_IGN)處理,也可將fd_stdin從監(jiān)聽隊列刪除 49 // close(fd); 50 exit(EXIT_SUCCESS); 51 ?//break;
52 }else 53 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)//如果沒接收到消息,打印關閉描述符,退出循環(huán) 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數(shù)組,里面存放監(jiān)聽描述符相關信息,供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)//如果沒接收到消息,打印關閉描述符,退出循環(huán) ????????????{ ????????????????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); } |
?
總結
以上是生活随笔為你收集整理的I/O复用的 select poll和epoll的简单实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都欢乐谷儿童票多少钱一张
- 下一篇: 几种并发服务器模型的实现:多线程,多进程