V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wudikua
V2EX  ›  程序员

epoll 在 EPOLLIN 的时候怎么确定数据已经读完

  •  
  •   wudikua · 2014-09-28 10:27:52 +08:00 · 5391 次点击
    这是一个创建于 3696 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我写了一个ECHO SERVER,用的是LT触发,在EPOLLIN的时候,通过4K一个BUFFER读数据,当读到的数据大于0,并且小于4K的时候就认为已经读完了,然后触发EPOLLOUT开始写数据。现在问题是当发送的数据长度是4K*N倍的时候,按照上面的逻辑会数据读完,但是没有触发EPOLLOUT,而EPOLL又因为句柄的接收队列上已经没有数据了而不会触发EPOLLIN了,这个是不是很无解??我想了一下,解决我这个问题有几种方法,第一种,如果有检测FD是不是还有数据的方法,但是不读,那我就可以在读到的字节数等于4K的时候检测一下是不是还有数据,如果没有数据了就EPOLLOUT。第二种,先读一个固定大小的头部,然后头部里写了数据的长度,这样根据已经读到的长度就知道是不是需要触发EPOLLOUT。第三种,客户端写数据故意不写BUFFER_SIZE * N大小,通过一些0x0来填充。求个正解的方法。
    11 条回复    2014-09-29 13:56:28 +08:00
    heiher
        1
    heiher  
       2014-09-28 11:36:23 +08:00 via Android
    为什么读完才out?
    heiher
        2
    heiher  
       2014-09-28 11:38:03 +08:00 via Android
    qsun
        3
    qsun  
       2014-09-28 11:45:21 +08:00
    socket设置成非堵塞,每次EPOLLIN事件发生都不停的读,直到read返回-1,并且 errono 是 EAGAIN 或者 EWOULDBLOCK,这样才代表socket中没有数据了。具体的可以看 READ(2)

    另外,你说的一个数据块4k,socket并不会buffer这些数据,所以每次都去可能是4k可能不是4k,1~4k都是可能的。所以你需要人肉管理对应的接收到的数据buffer。
    wudikua
        4
    wudikua  
    OP
       2014-09-28 12:54:08 +08:00
    @heiher 这个是一边READ一边WIRTE,如果要求把数据READ完才知道要WRITE什么,你这样就不行了。
    wudikua
        5
    wudikua  
    OP
       2014-09-28 12:58:36 +08:00
    @qsun 你说的这个和我说的没关系啊,如何在非阻塞的SOCKET上READ我已经处理了,我的问题是我读了一个BUFFER,返回了已经读的字节数是BUFFER的长度,怎么知道返回读到的字节数是因为已经读完所以返回BUFFER_SIZE,还是因为还有数据所以返回BUFFER_SIZE
    cloveryume
        6
    cloveryume  
       2014-09-28 13:03:28 +08:00
    TCP?TCP没有读完的概念,除非说关闭。
    TCP是一个流,肯定还要有更上一层的应用层协议,报头+报体,报头长度固定,内部有个字段表示报体的长度。可以甄别一个完整的报文。
    heiher
        7
    heiher  
       2014-09-28 13:07:32 +08:00
    @wudikua 你的这种情况是传输的数据是以一定的长度为一个 message 的,那你就增加 header 吧,然后在处理那边如果需要处理一个完整的 message 的话,只能使用一个 context 保存着 buffer list 或使用一个足够大的 buffer 存储 message。
    wudikua
        8
    wudikua  
    OP
       2014-09-28 13:47:33 +08:00
    @cloveryume 看来应该是这样~想再问个问题,报体如果不是一次WRITE,而是按照BUFFER分段WRITE,在服务器接收的时候是按照WRITE的顺序接收的么,还是可能会乱序,还得像TCP一样加个序号。
    cloveryume
        9
    cloveryume  
       2014-09-28 16:56:11 +08:00
    @wudikua 按照顺序,前提是中间没有穿插其他线程的写入。也需要检查write调用的返回值。
    比较常见的一种做法是一个连接对应一个outbuf,写数据的时候写到这个outbuf里。
    同时检测EPOLLOUT事件,可写时,就从outbuf里拿,往底层socket write。
    songco
        10
    songco  
       2014-09-29 13:44:02 +08:00
    最好用第二种方式, tcp是流, 应用层协议要自己知道什么时候一个消息读完整了. 比如特殊的结束符, 比如先发四字节的长度.

    如果消息是同步的, 应该有办法不加长度信息也能完成你说的, 不过比较麻烦, 如果是异步的就必须加了.
    luoqeng
        11
    luoqeng  
       2014-09-29 13:56:28 +08:00
    一般的网络库读了数据之后要更新下写的回调函数
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2748 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:48 · PVG 15:48 · LAX 23:48 · JFK 02:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.