contestjia 发表于 2015-06-28 09:08

Linux下epoll的一些问题

小弟使用EPOLLIN来触发可读事件,按照网上说的,ET模式下,只有缓冲区从不可读变成可读才会触发EPOLLIN时间,可现在写了一个测试程序,客户端每次发多个字节,服务器每次只收一个字节,但每次都会触发EPOLLIN事件,如果只收一个字节,缓冲区还有数据,下次发送就应该不会再触发EPOLLIN事件(ET模式下),服务端代码如下:#include <iostream>
#include <cstring>
#include <sys/epoll.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>

int main(void)
{
    int serfd = socket(AF_INET, SOCK_STREAM, 0);
    int clifd;
    int ret;
    if(serfd < 0)
      std::cout << "Create serfd fail!\n";

    struct sockaddr_in cliaddr, seraddr;
    bzero(&seraddr, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(12439);
    seraddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int on = 1;
    setsockopt(serfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
    bind(serfd, (struct sockaddr*)&seraddr, sizeof(seraddr));
    listen(serfd, 20);

    socklen_t len = sizeof(cliaddr);
    bzero(&cliaddr, sizeof(seraddr));
    clifd = accept(serfd, (struct sockaddr*)&cliaddr, &len);
    if(clifd < -1){
      std::cout << strerror(errno) << std::endl;
      close(serfd);
      return 0;
    }

    fcntl(clifd, F_SETFL, O_NONBLOCK);

    //Create epoll
    int epfd = epoll_create(1024);
    struct epoll_event ev;
    struct epoll_event events;
    ev.events = EPOLLET | EPOLLIN ;
    ev.data.fd = clifd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev);

    while(1)
    {
      int num = epoll_wait(epfd, events, 1024, -1);
      char buf = {0};
      for(int i = 0; i < num; i++)
      {
            if(events.events & EPOLLIN){
                std::cout << "Ready to read!\n";
                std::cout << "size=" << read(events.data.fd, buf, 1)
                        << "\tbuf=[" << buf << ']' << std::endl;
            }
            if(events.events & EPOLLOUT){
                std::cout << "Ready to write!\n";
            }
      }

    }
    return 0;
}

客户端使用: nc 来连接,并发送数据,第一次发了abcd,触发了EPOLLIN,但服务器只收一个字节,第二次发了ef,依然触发了EPOLLIN。

服务器端的接收情况:

此外:如果我注册的时候是EPOLLET|EPOLLOUT ,为什么这个EPOLLOUT每次都会触发?
谢谢各位!

tc1989tc 发表于 2015-06-28 11:30

hi,
按照EPOLLET 代码实现,应该是如果你没有读取完毕
并且底层驱动也没有数据发送过来的哈。
这时候你的epoll_wait 是不能等待相关的事件啦。
例如,你在读取一个字节后,你客服端不发送消息过来,那么你在epoll_wait
对应的文件符 是一直没有相应的事件报告的(在EPOLLET模式下)
如果你不使用EPOLLET模式,那么你在读取一个字节后,再次epoll_wait,
即使你的客服端不发送消息,也会有读事件报告的(因为你还有数据没读完)

contestjia 发表于 2015-06-28 12:29

回复 2# tc1989tc
嗯,是的,比如我客户端传了5个字节,epoll服务器第一次触发EPOLLIN的时候,我只收了1个字节,
这样的话,不是还有4个字节留在内核缓冲区中吗(网上说要触发EPOLLIN,一直要读到返回EAGAIN错误)?
下次客户端再发数据,由于是EPOLLET的,但此时内核缓冲区不为空(还有4个字节),没有从不可读变成可读,那它应该不会再次触发EPOLLIN啊!
可我现在做的测试结果,刚好相反!
   

tc1989tc 发表于 2015-06-28 15:26

看linux的evenepoll.c 里面的实现
和你测试的结果是一致的。
它只保证啦一次报文的边缘触发
也就是驱动有报文来有等待队列 他还是会**的

contestjia 发表于 2015-06-28 15:44

回复 4# tc1989tc
嗯勒,谢谢你!


   

KingaChou 发表于 2015-09-02 09:09

页: [1]
查看完整版本: Linux下epoll的一些问题