之前写过一篇关于Linux下面采用epoll设计服务器应用程序的文章,时隔很久了,最近发现一个问题,是关于read函数读取报文时候问题,这里提出来。
epoll并不像windows下的完成端口那样,可以通知用户此次事件(收数据或者发数据)的数据量有多少,因此在读取数据的时候就比较麻烦了,因为epoll通知你只是有新数据到达了,并不告诉你有多少,因此需要应用层运用循环不断的收数据,知道结束。结合下面的源代码进行分析一下。
while(m_RcvedLen < TotalSize) {
    if((Rcv = read(m_sClient, m_Buffer, (TotalSize - m_RcvedLen))) < 0) {
      if(EAGAIN == errno) {
        break;
      }
      else {
        perror("read fail");
        close(m_sClient);
        return -1;
      }
    }
    else if(0 == Rcv){
      close(m_sClient);        
      return -2;
    }
    else {
      m_RcvedLen += Rcv;
    }
  }
 
上面的代码是先读报头,后读报体的。原来是直接读,给read函数的第三个参数一个很大的值,知道read函数返回错误EAGAIN才表示没有数据可以读了,但是这样会出现一个问题,当客户端发送了数据之后就直接关闭socket,此时服务端第一包读取正常,这时循环继续读下一报,此时read函数不会返回EAGAIN,而是返回0,表示客户端已经关闭连接了。所以,在服务端读取的时候,一定要先读报文,从报文中获得此次报文的大小,然后依次来作为接收结束的判断条件。这样上层就可以根据read函数的返回值不同,区分出(1)还有报文可读(2)报文已经读完(3)对方关闭socket三种情况了。