epoll 在 EPOLLIN 的时候怎么确定数据已经读完 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
wudikua
V2EX    程序员

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

  •  
  •   wudikua 2014-09-28 10:27:52 +08:00 5850 次点击
    这是一个创建于 4106 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我写了一个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     Solana     2679 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 15:19 PVG 23:19 LAX 07:19 JFK 10:19
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86