epoll在断开客户端连接时循环 [英] epoll loops on disconnection of a client

查看:140
本文介绍了epoll在断开客户端连接时循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用epoll实现套接字服务器.我有2个线程在执行2个任务:

I am trying to implement a socket server by using epoll. I have 2 threads doing 2 tasks:

  1. 监听传入的连接
  2. 在屏幕上写出客户端正在发送的数据.

为进行测试,我将客户端和服务器安装在运行3或4个客户端的同一台计算机上. 服务器运行良好,直到我没有通过发出CTRL-C杀死其中一个客户端:我立即开始,服务器开始循环并以非常快的速度打印来自其他客户端的数据.奇怪的是,

For my test I have the client and the server on the same machine with 3 or 4 clients running. The server works fine until I don't kill one of the client by issuing a CTRL-C: as soon I do that the server starts looping and printing at a very fast rate data from other client. The strange thing is that

  1. 客户端每2秒发送一次数据,但是服务器的速率更高
  2. epoll_wait还应该在客户端之一断开连接时打印某些内容,因为它也在检查EPOLLHUP或EPOLLERR
  3. epoll_wait应该稍等一会儿再打印,因为我给他超时了3000毫秒.
  1. the client sends data each 2 seconds but the rate of the server is higher
  2. epoll_wait is also supposed to print something when one of the client disconnects as it is checking also for EPOLLHUP or EPOLLERR
  3. epoll_wait should wait a bit before printing since I gave him a timeout of 3000 milliseconds.

可以帮忙吗?可能是我以错误的方式将epoll描述符传递给了另一个线程吗?我无法理解,因为代码看起来与周围的许多示例相似.

Can you help? Could it be that I am passing in a wrong way the epoll descriptor to the other thread? I cannot understand since the code looks similar to many examples around.

非常感谢

 // server.cpp
 #include <iostream>
 #include <cstdio>
 #include <cstring>
 extern "C" {
 #include <sys/epoll.h>    
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <netdb.h>
 #include <pthread.h>
 }

 #define MAX_BACKLOG 10

 void* readerthread(void* args){
     int epfd = *((int*)args);
     epoll_event outwait[10];
     while(true){
         int retpw = epoll_wait( epfd, outwait,20, 3000 );
         if( retpw == -1 ){
         printf("epoll error %m\n");
         }else if( retpw == 0 ){
         printf("nothing is ready yet\n");
         continue;
         }else{
             for( int i=0;i<retpw;i++){
                 if( outwait[i].events & EPOLLIN ){
                     int fd = outwait[i].data.fd;   
                     char buf[64];
                     if( -1 == read(fd,buf,64) ){
                         printf("error reading %m\n");
                     }
                     printf("%s\n",buf);
                 }else{
                     std::cout << "other event" << std::endl;
                 }                  
             }
         }
     }  
 }

 int main(){ 

      int epfd = epoll_create(10);
      if( -1 == epfd ){
      std::cerr << "error creating EPOLL server" << std::endl;
      return -1;
      }

      pthread_t reader;
      int rt = pthread_create( &reader, NULL, readerthread, (void*)&epfd );
      if( -1 == rt ){
      printf("thread creation %m\n");
      return -1;
      }



      struct addrinfo addr;
      memset(&addr,0,sizeof(addrinfo));
      addr.ai_family   = AF_INET;
      addr.ai_socktype = SOCK_STREAM;
      addr.ai_protocol = 0;
      addr.ai_flags    = AI_PASSIVE;

      struct addrinfo * rp,* result;
      getaddrinfo( "localhost","59000",&addr,&result );
      for( rp = result; rp != NULL; rp = rp->ai_next ){

      // we want to take the first ( it could be IP_V4 
      // or IP_V6 )
      break;
      }     

      int sd = socket( AF_INET, SOCK_STREAM, 0 );
      if(-1==sd ){
      std::cerr << "error creating the socket" << std::endl;
      return -1;        
      }
      // to avoid error 'Address already in Use'
      int optval = 1;
      setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));         

      if( -1==bind( sd, result->ai_addr, result->ai_addrlen ) ){
      printf("%m\n");
      std::cerr << "error binding" << std::endl;
      return -1;
      }

      while(true){

           std::cout << "listen" << std::endl;
           if( -1== listen(sd, MAX_BACKLOG ) ){
               std::cerr << "listen didn't work" << std::endl;
               return -1;
           }

           std::cout << "accept" << std::endl;
           sockaddr peer;
              socklen_t addr_size; 
           int pfd = accept( sd, &peer ,&addr_size );
           if( pfd == -1 ){
               std::cerr << "error calling accept()" << std::endl;
               return -1;
           }
           epoll_event ev;
           ev.data.fd = pfd;
           ev.events =  EPOLLIN;
           std::cout << "adding to epoll list" << std::endl;
           if( -1 == epoll_ctl( epfd, EPOLL_CTL_ADD, pfd, &ev ) ){
               printf("epoll_ctl error %m\n");
               return -1;
           }

      }

 }
 // end of server.cpp



 // client.cpp
     #include <iostream>
 #include <cstring>
 #include <cstdio>
 extern "C"{
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <netdb.h>
 }

 int main(){

      const char* servername = "localhost";
      const char* serverport = "59000";

      struct addrinfo server_address;
      memset( &server_address, 0, sizeof(struct addrinfo) );
      server_address.ai_family  =  AF_INET;
      server_address.ai_socktype =  SOCK_STREAM;
      server_address.ai_protocol  =  0; // any protocol
      server_address.ai_flags    =  0;

      struct addrinfo * result, * rp;

      int res = getaddrinfo( servername, serverport, &server_address, &result );
      if( -1 == res ){
         std::cout << "I cannot getaddress " << servername << std::endl;
         return -1;
      }

      int fd = socket( server_address.ai_family
                , server_address.ai_socktype
                , server_address.ai_protocol );
      if( -1 == fd ){
      printf("I cannot open a socket %m\n");
            return -1;
      } 

     for( rp = result; rp != NULL; rp = rp->ai_next ){
         std::cout << "************" << std::endl;
        if( -1 == connect( fd, rp->ai_addr, rp->ai_addrlen ) ){
             close(fd);
         }else{
         std::cout << "connected" << std::endl;
         break;
       }
     }
     if( rp == NULL ){
         std::cerr << "I couldn't connect server " << servername << std::endl;
     }
     while(true){
         sleep(2);
         pid_t me = getpid();
         char buf[64];
         bzero( buf,sizeof(buf));
         sprintf( buf,"%ld",me );
         write(fd,buf,sizeof(buf));
         printf("%s\n",buf);
      }
 }
 // end of client.cpp

推荐答案

客户端断开连接是由文件描述符上的EOF条件发出的.系统认为EOF是文件描述符为可读"状态.但是,当然,无法读取EOF条件.这是循环的来源. epoll的行为就像始终可读的断开连接的客户端的文件描述符一样.通过检查read何时返回读取的0个字节,您可以检测到您处于EOF条件.

A client disconnection is signalled by an EOF condition on the file descriptor. The system considers EOF to be a state in which the file descriptor is 'readable'. But, of course, the EOF condition cannot be read. This is the source of your looping. epoll is acting like the file descriptor for the disconnected client is always readable. You can detect that you have an EOF condition by checking when read returns 0 bytes read.

处理EOF条件的唯一方法是以某种方式close文件描述符. shutdown(sockfd, SHUT_RDWR)close(sockfd);可能取决于事物的流动方式.

The only way to deal with an EOF condition is to close the file descriptor in some way. Depending on exactly how the flow of things go, this could be with shutdown(sockfd, SHUT_RD), shutdown(sockfd, SHUT_RDWR) or close(sockfd);.

除非您知道您需要 shutdown(2) 出于任何原因致电,我建议您使用close.当然,您应该记得在close之前告诉epoll不再需要文件描述符.我不确定如果不这样做会发生什么,但是一种可能性是epoll会出错.另一个是epoll将神秘地开始报告具有相同数值的新文件描述符的事件,然后再将其添加到epoll应该关心的列表中.

Unless you know that you need the shutdown(2) call for whatever reason, I would recommend you use close. Of course, you should remember to tell epoll that the file descriptor is no longer of interest before you close. I'm not sure what will happen if you don't, but one possibility is that epoll will error. Another is that epoll will mysteriously begin reporting events for a new file descriptor that has the same numeric value before you add it to the list epoll should care about.

这篇关于epoll在断开客户端连接时循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆