採用epoll實現echo server和client

 echo server,採用epoll模型,先讀後寫

 

  1. #include <iostream> 
  2. #include <sys/socket.h>  
  3. #include <sys/epoll.h>  
  4. #include <netinet/in.h>  
  5. #include <arpa/inet.h>  
  6. #include <fcntl.h>  
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <errno.h>  
  10. using namespace std; 
  11.  
  12. #define MAXLINE 512  
  13. #define OPEN_MAX 100  
  14. #define LISTENQ 20  
  15. #define SERV_PORT  9876 
  16. #define INFTIM 1000  
  17.  
  18. void setnonblocking(int sock)  
  19. {  
  20.     int opts;  
  21.     opts=fcntl(sock,F_GETFL);  
  22.  
  23.     if(opts<0)  
  24.     {  
  25.         perror("fcntl(sock,GETFL)");  
  26.         exit(1);  
  27.     }  
  28.  
  29.     opts = opts | O_NONBLOCK;  
  30.  
  31.     if(fcntl(sock,F_SETFL,opts)<0)  
  32.     {  
  33.         perror("fcntl(sock,SETFL,opts)");  
  34.         exit(1);  
  35.     }  
  36. }  
  37.  
  38.  
  39.  
  40.  
  41. /* 
  42.    假如發送端流量大於接收端的流量 
  43.    (意思是epoll所在的程序讀比轉發的socket要快), 
  44.    由於是非阻塞的socket,那麼send()函數雖然返回, 
  45.    但實際緩衝區的數據並未真正發給接收端, 
  46.    這樣不斷的讀和發, 
  47.    當緩衝區滿後會產生EAGAIN錯誤(參考man send),同時, 
  48.    不理會這次請求發送的數據.所以, 
  49.    需要封裝socket_send()的函數用來處理這種情況, 
  50.    該函數會盡量將數據寫完再返回,返回-1表示出錯。 
  51.    在socket_send()內部,當寫緩衝已滿(send()返回-1,且errno爲EAGAIN), 
  52.    那麼會等待後再重試.這種方式並不很完美, 
  53.    在理論上可能會長時間的阻塞在socket_send()內部, 
  54.    但暫沒有更好的辦法. 
  55.  
  56.  */ 
  57.  
  58. ssize_t socket_send(int sockfd, const char* buffer, size_t buflen) 
  59.     ssize_t tmp; 
  60.     size_t total = buflen; 
  61.     const char *p = buffer; 
  62.  
  63.     while(1) 
  64.     { 
  65.         tmp = send(sockfd, p, total, 0); 
  66.         if(tmp < 0) 
  67.         { 
  68.             // 當send收到信號時,可以繼續寫,但這裏返回-1. 
  69.             if(errno == EINTR) 
  70.                 //return -1;zxd 
  71.                 continue
  72.  
  73.             // 當socket是非阻塞時,如返回此錯誤,表示寫緩衝隊列已滿, 
  74.             // 在這裏做延時後再重試. 
  75.             if(errno == EAGAIN) 
  76.             { 
  77.                 usleep(1000); 
  78.                 continue
  79.             } 
  80.  
  81.             return -1; 
  82.         } 
  83.  
  84.         if((size_t)tmp == total) 
  85.             return buflen; 
  86.  
  87.         total -= tmp; 
  88.         p += tmp; 
  89.     } 
  90.  
  91.     return tmp; 
  92.  
  93.  
  94. void epoll_ctl_err_show()   
  95. {   
  96.     std::cout << "error at epoll_ctl" << std::endl;   
  97.     if(EBADF == errno)   
  98.     {   
  99.         std::cout << "error at epoll_ctl, error  is EBADF" << std::endl;   
  100.     }   
  101.     else if(EEXIST == errno)   
  102.     {   
  103.         std::cout << "error at epoll_ctl, error  is EEXIST" << std::endl;   
  104.     }   
  105.     else if(EINVAL == errno)   
  106.     {   
  107.         std::cout << "error at epoll_ctl, error  is EINVAL" << std::endl;   
  108.     }   
  109.     else if(ENOENT == errno)   
  110.     {   
  111.         std::cout << "error at epoll_ctl, error  is ENOENT" << std::endl;   
  112.     }   
  113.     else if(ENOMEM == errno)   
  114.     {   
  115.         std::cout << "error at epoll_ctl, error  is ENOMEM" << std::endl;   
  116.     }   
  117.     else if(ENOSPC == errno)   
  118.     {   
  119.         std::cout << "error at epoll_ctl, error  is ENOSPC" << std::endl;   
  120.     }   
  121.       
  122. }  
  123.  
  124. int main()  
  125. {  
  126.     int i, maxi, listenfd, connfd, sockfd, epfd, nfds;  
  127.     ssize_t n;  
  128.     char line[MAXLINE];  
  129.     socklen_t clilen;  
  130.  
  131.     struct epoll_event ev,events[20]; //聲明epoll_event結構體的變量, ev用於註冊事件, events數組用於回傳要處理的事件  
  132.     epfd=epoll_create(256); //生成用於處理accept的epoll專用的文件描述符, 指定生成描述符的最大範圍爲256  
  133.  
  134.     struct sockaddr_in clientaddr;  
  135.     struct sockaddr_in serveraddr;  
  136.  
  137.     listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  138.  
  139.     setnonblocking(listenfd); //把用於監聽的socket設置爲非阻塞方式  
  140.  
  141.     ev.data.fd=listenfd; //設置與要處理的事件相關的文件描述符  
  142.     ev.events=EPOLLIN | EPOLLET; //設置要處理的事件類型  
  143.     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); //註冊epoll事件 
  144.  
  145.     bzero(&serveraddr, sizeof(serveraddr));  
  146.     serveraddr.sin_family = AF_INET;  
  147.     char *local_addr="127.0.0.1";  
  148.     inet_aton(local_addr,&(serveraddr.sin_addr)); 
  149.     serveraddr.sin_port=htons(SERV_PORT);  //或者htons(SERV_PORT);  
  150.  
  151.     bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));  
  152.  
  153.     listen(listenfd, LISTENQ);  
  154.  
  155.     maxi = 0;  
  156.  
  157.     for( ; ; ) {  
  158.         nfds=epoll_wait(epfd, events, 20, -1); //等待epoll事件的發生  
  159.          
  160.  
  161.         for(i=0;i<nfds;++i) //處理所發生的所有事件  
  162.         {  
  163.             if(events[i].data.fd==listenfd)    /**監聽事件**/ 
  164.             {  
  165.                 //循環accept 
  166.                 while((connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen)) > 0) 
  167.                 { 
  168.                     setnonblocking(connfd); //把客戶端的socket設置爲非阻塞方式 
  169.  
  170.                     char *str = inet_ntoa(clientaddr.sin_addr);  
  171.                     std::cout<<"connect from "<<str<<std::endl;  
  172.  
  173.                     ev.data.fd=connfd; //設置用於讀操作的文件描述符  
  174.                     ev.events=EPOLLIN | EPOLLET; //設置用於注測的讀操作事件  
  175.                     epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //註冊ev事件  
  176.                 } 
  177.             }  
  178.             else if(events[i].events&EPOLLIN)     /**讀事件**/ 
  179.             {  
  180.                 //fprintf(stderr, "EPOLLIN................%d\n", maxi++); 
  181.                 if ( (sockfd = events[i].data.fd) < 0)  
  182.                 { 
  183.                     continue;  
  184.                 } 
  185.                 memset(line, 0, MAXLINE); 
  186.                 n = 0; 
  187.                 int nread = 0; 
  188.                 while((nread= read(sockfd, line + n, MAXLINE)) > 0) 
  189.                 { 
  190.                     n += nread; 
  191.                 }//讀到EAGAIN,說明讀完了 
  192.  
  193.  
  194.                 if(nread == -1 && errno != EAGAIN) 
  195.                 { 
  196.                     epoll_ctl_err_show(); 
  197.                     std::cout<<"readline error"<<std::endl; 
  198.                     close(sockfd); //關閉一個文件描述符,那麼它會從epoll集合中自動刪除 
  199.                     //描述符關閉後,後面的邋邋邋邋EPOLLOUT設置了,但不起作用了 
  200.                     events[i].data.fd = -1;  
  201.                 } 
  202.  
  203.                 //這裏要加上判斷,nread爲0時,說明客戶端已經關閉 
  204.                 //此時,需要關閉描述符,否則在/proc/id/fd下能看到描述符會一直存在 
  205.                 if(nread == 0) 
  206.                 { 
  207.                     close(sockfd); 
  208.                     continue
  209.                 } 
  210.  
  211.  
  212.                 ev.data.fd=sockfd; //設置用於寫操作的文件描述符  
  213.                 ev.events=EPOLLOUT | EPOLLET; //設置用於注測的寫操作事件  
  214.                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改sockfd上要處理的事件爲EPOLLOUT  
  215.             }  
  216.             else if(events[i].events & EPOLLOUT)    /**寫事件**/ 
  217.             {  
  218.                 sockfd = events[i].data.fd;  
  219.                 //write(sockfd, line, n); orig 
  220.  
  221.                 int iRet = socket_send(sockfd, line, strlen(line) + 1); 
  222.                 if(iRet == -1 || iRet != strlen(line) + 1) 
  223.                 { 
  224.                     perror("write error!"); 
  225.                 }/*zxd*/ 
  226.  
  227.                 ev.data.fd=sockfd; //設置用於讀操作的文件描述符  
  228.                 ev.events=EPOLLIN | EPOLLET; //設置用於註冊的讀操作事件  
  229.                 epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev); //修改sockfd上要處理的事件爲EPOLIN  
  230.             }  
  231.         }  
  232.     }  

對於server的實現,是先讀後寫的模式,以後再將sfd設成EPOLLIN|EPOLLOUT|EPOLLET的模式再實驗一下。

epoll_wait在客戶端關閉後會返回,EPOLLIN被激活,此時nread爲0,因客戶端已經關閉,所以需要把sfd關閉。

nread爲-1且errno==EAGAIN,說明數據已經讀完,設置EPOLLOUT。

與之對應的echo client,先寫後讀,採用epoll模式:

 

  1. #include "TCPUtility.h" 
  2. #include <netinet/in.h> 
  3. #include <sys/types.h> 
  4. #include <sys/socket.h> 
  5. #include <arpa/inet.h> 
  6. #include <sys/epoll.h> 
  7. #include <errno.h> 
  8. #include <string.h> 
  9. #include <fcntl.h> 
  10.  
  11.  
  12. #define BUFSIZE 512  
  13.  
  14.  
  15. void SetNonBlock(int fd) 
  16.     int flag = fcntl ( fd, F_GETFL, 0 ); 
  17.     fcntl ( fd, F_SETFL, flag | O_NONBLOCK ); 
  18.  
  19. int main(int argc, char** argv) 
  20.     int iRet = RET_OK; 
  21.  
  22.     if(4 != argc) 
  23.     { 
  24.         Debug_UserLog("Parameter: ServerIP Message ServerPort", RET_ERR); 
  25.         return RET_ERR; 
  26.     } 
  27.  
  28.     in_port_t i16_port = atoi(argv[3]); 
  29.     if(0 >= i16_port) 
  30.     { 
  31.         Debug_UserLog("port number is wrong", RET_ERR); 
  32.         return RET_ERR; 
  33.     } 
  34.  
  35.     int sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
  36.     if(-1 == sk) 
  37.     { 
  38.         Debug_SysLog("open socket failed!"); 
  39.         return RET_ERR; 
  40.     } 
  41.  
  42.  
  43.     struct sockaddr_in sa = {0}; 
  44.     sa.sin_family = AF_INET; 
  45.     sa.sin_port = htons(i16_port); 
  46.  
  47.     struct sockaddr_in *psa = &sa; 
  48.  
  49.     iRet = inet_pton(AF_INET, argv[1], &psa->sin_addr.s_addr); 
  50.     if(0 == iRet) 
  51.     { 
  52.         Debug_UserLog("inet_pton failed, invalid address!", RET_ERR); 
  53.         close(sk); 
  54.         return RET_ERR; 
  55.     } 
  56.     else if(iRet < 0) 
  57.     { 
  58.         Debug_SysLog("inet_pton failed"); 
  59.         close(sk); 
  60.         return RET_ERR; 
  61.     } 
  62.  
  63.     if(connect(sk, (struct sockaddr*)&sa, sizeof(sa)) < 0) 
  64.     { 
  65.         Debug_SysLog("connect failed"); 
  66.         close(sk); 
  67.         return RET_ERR; 
  68.     } 
  69.  
  70.     SetNonBlock(sk); 
  71.  
  72.     int efd;  
  73.     efd = epoll_create(10);  
  74.     if(efd == -1) 
  75.     { 
  76.         perror("epoll_create error!"); 
  77.         exit(1); 
  78.     } 
  79.  
  80.     struct epoll_event event; 
  81.     struct epoll_event events[10]; 
  82.  
  83.     event.events = EPOLLOUT | EPOLLIN | EPOLLET; 
  84.     event.data.fd = sk; 
  85.  
  86.     epoll_ctl(efd, EPOLL_CTL_ADD, sk, &event); 
  87.  
  88.  
  89.     getchar(); 
  90.     int loop = 0; 
  91.     while(1) 
  92.     { 
  93.         ssize_t numBytesRcvd = 0; 
  94.         char buffer[BUFSIZE] = {0}; 
  95.         int n = 0; 
  96.         int i = 0; 
  97.  
  98.         if(loop == 1) 
  99.         { 
  100.             break
  101.         } 
  102.   
  103.         n = epoll_wait(efd, events, 10, -1); 
  104.  
  105.         printf("%d\n", n); 
  106.  
  107.         for(i = 0; i < n; i++) 
  108.         { 
  109.             if(events[i].events & EPOLLOUT) 
  110.             { 
  111.                 printf("EPOLLOUT...............\n"); 
  112.                 snprintf(buffer, BUFSIZE, "i am process %d, just say: %s\n", getpid(), argv[2]); 
  113.  
  114.                 int n = strlen(buffer); 
  115.                 int nsend = 0; 
  116.  
  117.                 while(n > 0) 
  118.                 { 
  119.                     //nsend = send(events[i].data.fd, buffer + nsend, n, 0); 
  120.                     nsend = write(events[i].data.fd, buffer + nsend, n); 
  121.                     if(nsend < 0 && errno != EAGAIN) 
  122.                     { 
  123.  
  124.                         Debug_SysLog("send failed"); 
  125.                         close(events[i].data.fd); 
  126.                         return RET_ERR; 
  127.                     } 
  128.                     n -= nsend; 
  129.                 } 
  130.             } 
  131.  
  132.             if(events[i].events & EPOLLIN) 
  133.             { 
  134.                 printf("EPOLLIN...............\n"); 
  135.                 memset(buffer, 0, BUFSIZE); 
  136.  
  137.                 int len = strlen(buffer); 
  138.                 int n = 0; 
  139.                 int nrecv = 0; 
  140.  
  141.                 //while((nrecv = recv(events[i].data.fd, buffer + n, BUFSIZE - 1, 0)) > 0) 
  142.                 while(1){ 
  143.                     nrecv = read(events[i].data.fd, buffer + n, BUFSIZE - 1) ; 
  144.                     if(nrecv == -1 && errno != EAGAIN) 
  145.                     { 
  146.                         perror("read error!"); 
  147.                     } 
  148.                     if((nrecv == -1 && errno == EAGAIN) || nrecv == 0) 
  149.                     { 
  150.                         break
  151.                     } 
  152.                     n += nrecv; 
  153.                 } 
  154.                 loop = 1; 
  155.                 printf("%s\n", buffer); 
  156.             } 
  157.         } 
  158.     } 
  159.     close(sk); 
  160.     close(efd); 
  161.     return RET_OK; 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章