多进程服务器中,epoll的创建应该在创建子进程之后

转:http://blog.csdn.net/wei801004/archive/2009/06/13/4264031.aspx

看我的测试代码,似乎应该是在创建子进程之后创建epoll的fd,否则程序将会有问题,试将代码中两个CreateWorker函数的调用位置分别调用,一个在创建epoll fd之前,一个在之后,在调用在创建之前的代码会出问题,在我的机器上(linux内核2.6.26)表现的症状就是所有进程的epoll_wait函数返回0, 而客户端似乎被阻塞了:

服务器端:

 

view plain copy to clipboard print ?
  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. #include <sys/types.h>  
  11. #include <sys/wait.h>  
  12. using namespace std;  
  13. #define MAXLINE 5  
  14. #define OPEN_MAX 100  
  15. #define LISTENQ 20  
  16. #define SERV_PORT 5000  
  17. #define INFTIM 1000  
  18. typedef struct task_t  
  19. {  
  20.     int fd;  
  21.     char buffer[100];  
  22.     int n;  
  23. }task_t;  
  24. int CreateWorker(int nWorker)  
  25. {  
  26.     if (0 < nWorker)  
  27.     {  
  28.         bool bIsChild;  
  29.         pid_t nPid;  
  30.         while (!bIsChild)  
  31.         {  
  32.             if (0 < nWorker)  
  33.             {  
  34.                 nPid = ::fork();  
  35.                 if (nPid > 0)  
  36.                 {  
  37.                     bIsChild = false;  
  38.                     --nWorker;  
  39.                 }  
  40.                 else if (0 == nPid)  
  41.                 {  
  42.                     bIsChild = true;  
  43.                     printf("create worker %d success!/n", ::getpid());  
  44.                 }  
  45.                 else  
  46.                 {  
  47.                     printf("fork error: %s/n", ::strerror(errno));  
  48.                     return -1;  
  49.                 }  
  50.             }  
  51.             else   
  52.             {  
  53.                 int nStatus;  
  54.                 if (-1 == ::wait(&nStatus))  
  55.                 {  
  56.                     ++nWorker;  
  57.                 }  
  58.             }  
  59.         }  
  60.     }  
  61.     return 0;  
  62. }  
  63. void setnonblocking(int sock)  
  64. {  
  65.     int opts;  
  66.     opts=fcntl(sock,F_GETFL);  
  67.     if(opts<0)  
  68.     {  
  69.         perror("fcntl(sock,GETFL)");  
  70.         exit(1);  
  71.     }  
  72.     opts = opts|O_NONBLOCK;  
  73.     if(fcntl(sock,F_SETFL,opts)<0)  
  74.     {  
  75.         perror("fcntl(sock,SETFL,opts)");  
  76.         exit(1);  
  77.     }     
  78. }  
  79. int main()  
  80. {  
  81.     int i, maxi, listenfd, connfd, sockfd,epfd,nfds;  
  82.     ssize_t n;  
  83.     char line[MAXLINE];  
  84.     socklen_t clilen;  
  85.     struct epoll_event ev,events[20];  
  86.     struct sockaddr_in clientaddr;  
  87.     struct sockaddr_in serveraddr;  
  88.     listenfd = socket(AF_INET, SOCK_STREAM, 0);  
  89.        bzero(&serveraddr, sizeof(serveraddr));  
  90.     serveraddr.sin_family = AF_INET;  
  91.     char *local_addr="127.0.0.1";  
  92.     inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);  
  93.     serveraddr.sin_port=htons(SERV_PORT);  
  94.       // 地址重用  
  95.     int nOptVal = 1;  
  96.     socklen_t nOptLen = sizeof(int);  
  97.     if (-1 == ::setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &nOptVal, nOptLen))  
  98.     {  
  99.         return -1;  
  100.     }      
  101.     setnonblocking(listenfd);  
  102.     bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));  
  103.     listen(listenfd, LISTENQ);      
  104.       
  105.     CreateWorker(5);  
  106.       
  107.     //把socket设置为非阻塞方式  
  108.       
  109.     //生成用于处理accept的epoll专用的文件描述符  
  110.     epfd=epoll_create(256);      
  111.     //设置与要处理的事件相关的文件描述符  
  112.     ev.data.fd=listenfd;  
  113.     //设置要处理的事件类型  
  114.     ev.events=EPOLLIN|EPOLLET;  
  115.     //ev.events=EPOLLIN;  
  116.     //注册epoll事件  
  117.     epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);  
  118.    
  119.      //CreateWorker(5);  
  120.        
  121.     maxi = 0;  
  122.       
  123.     task_t task;   
  124.     task_t *ptask;  
  125.     while(true)   
  126.     {  
  127.         //等待epoll事件的发生  
  128.         nfds=epoll_wait(epfd,events,20,500);  
  129.         //处理所发生的所有事件       
  130.         for(i=0;i<nfds;++i)  
  131.         {  
  132.             if(events[i].data.fd==listenfd)  
  133.             {                  
  134.                 connfd = accept(listenfd,NULL, NULL);  
  135.                 if(connfd<0){                      
  136.                     printf("connfd<0, listenfd = %d/n", listenfd);  
  137.                     printf("error = %s/n", strerror(errno));  
  138.                     exit(1);  
  139.                 }  
  140.                 setnonblocking(connfd);  
  141.                  
  142.                 //设置用于读操作的文件描述符  
  143.                 memset(&task, 0, sizeof(task));  
  144.                 task.fd = connfd;  
  145.                 ev.data.ptr = &task;  
  146.                 //设置用于注册的读操作事件  
  147.                 ev.events=EPOLLIN|EPOLLET;  
  148.                 //ev.events=EPOLLIN;  
  149.                 //注册ev  
  150.                 epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);  
  151.             }  
  152.             else if(events[i].events&EPOLLIN)  
  153.             {  
  154.                 cout << "EPOLLIN" << endl;  
  155.                 ptask = (task_t*)events[i].data.ptr;  
  156.                 sockfd = ptask->fd;  
  157.                   
  158.                 if ( (ptask->n = read(sockfd, ptask->buffer, 100)) < 0) {  
  159.                     if (errno == ECONNRESET) {  
  160.                         close(sockfd);  
  161.                         events[i].data.ptr = NULL;  
  162.                     } else  
  163.                         std::cout<<"readline error"<<std::endl;  
  164.                 } else if (ptask->n == 0) {  
  165.                     close(sockfd);  
  166.                     events[i].data.ptr = NULL;  
  167.                 }  
  168.                 ptask->buffer[ptask->n] = '/0';  
  169.                 cout << "read " << ptask->buffer << endl;  
  170.                   
  171.                 //设置用于写操作的文件描述符                                  
  172.                 ev.data.ptr = ptask;  
  173.                 //设置用于注测的写操作事件  
  174.                 ev.events=EPOLLOUT|EPOLLET;  
  175.                                   
  176.                 //修改sockfd上要处理的事件为EPOLLOUT  
  177.                 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);  
  178.             }  
  179.             else if(events[i].events&EPOLLOUT)  
  180.             {     
  181.                 cout << "EPOLLOUT" << endl;  
  182.                 ptask = (task_t*)events[i].data.ptr;  
  183.                 sockfd = ptask->fd;  
  184.                   
  185.                 write(sockfd, ptask->buffer, ptask->n);  
  186.                   
  187.                 //设置用于读操作的文件描述符                
  188.                 ev.data.ptr = ptask;  
  189.                   
  190.                 //修改sockfd上要处理的事件为EPOLIN  
  191.                 epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev);  
  192.                 cout << "write " << ptask->buffer;  
  193.                 memset(ptask, 0, sizeof(*ptask));  
  194.                 close(sockfd);  
  195.             }  
  196.         }  
  197.     }  
  198.     return 0;  
  199. }  

 

测试客户端:

 

view plain copy to clipboard print ?
  1. #!/usr/bin/perl  
  2. use strict;  
  3. use Socket;  
  4. use IO::Handle;  
  5. sub echoclient  
  6. {  
  7.     my $host = "127.0.0.1";  
  8.     my $port = 5000;  
  9.     my $protocol = getprotobyname("TCP");  
  10.     $host = inet_aton($host);  
  11.     socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";  
  12.     my $dest_addr = sockaddr_in($port, $host);  
  13.     connect(SOCK, $dest_addr) or die "connect() failed: $!";  
  14.     SOCK->autoflush(1);  
  15.     my $msg_out = "hello world/n";  
  16.     print "out = ", $msg_out;  
  17.     print SOCK $msg_out;  
  18.     my $msg_in = <SOCK>;  
  19.     print "in = ", $msg_in;  
  20.     close SOCK;  
  21. }  
  22. #&echoclient;  
  23. #exit(0);  
  24. for (my $i = 0; $i < 9999; $i++)  
  25. {  
  26.     echoclient;  
  27. }  

 lighttpd 也是在创建完子进程后创建的epoll的fd.

你可能感兴趣的:(socket,Stream,服务器,null,events,linux内核)