tcp 半包、粘包问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
broadliyn
V2EX    问与答

tcp 半包、粘包问题

  •  
  •   broadliyn 2015 年 10 月 21 日 4094 次点击
    这是一个创建于 3737 天前的主题,其中的信息可能已经有所发展或是发生改变。

    做消息推送服务器,自己设计了一套私有协议。

    但是有一个问题。传统的 http 是一应一答的,消息推送是长链接的,如果客户端不按照规定的协议格式来传输数据,就会导致服务器端无法正常解析数据。

    因为拆包粘包的问题,一旦缓冲区的部分数据异常,那么后边数据就会全部出错都无法解析(因为缓冲区的数据都是连续的,根据特定字节长度来读取字段数据:readInt/readLong/readChar 等)。

    那么出现这样的错误数据无法解析,服务器端应该如何处理呢?
    我的想法是,一个消息协议以某个特殊字符串开头(如:$^&* ),当服务器数据解析出错时,那么就把错误的数据全部 discard ,直到遇到下一个“$^&*”为止。

    25 条回复    2015-10-22 07:21:36 +08:00
    morethansean
        1
    morethansean  
       2015 年 10 月 21 日
    程序不出错,缓冲区不会数据异常吧。
    sujin190
        2
    sujin190  
       2015 年 10 月 21 日
    关闭连接让客户端重新打开就是了,省得这么麻烦
    broadliyn
        3
    broadliyn  
    OP
       2015 年 10 月 21 日
    @sujin190 你好,除了重新打开客户端链接还有什么其他更好的办法吗?
    broadliyn
        4
    broadliyn  
    OP
       2015 年 10 月 21 日
    @morethansean 就怕有异常。
    skydiver
        5
    skydiver  
       2015 年 10 月 21 日
    TCP 本来就是流协议,需要自己设定包的边界。
    hyq
        6
    hyq  
       2015 年 10 月 21 日
    把一个包定义成[length]和[body],先读取 length ,然后根据 length ,读取 body 。
    hyq
        7
    hyq  
       2015 年 10 月 21 日
    如果怕客户端乱来,可以加个 checksum 或者来个加密后再 checksum
    broadliyn
        8
    broadliyn  
    OP
       2015 年 10 月 21 日
    @hyq 我现在的协议类似 http ,有 header (定长)和 body (变长)两部分, header 里带了 timestamp 、 body 的 length 、 body 的 checksum ,然后根据 length 去取剩余变长的 body 部分。
    现在主要是怕因为客户端乱来等原因, body 部分丢了一半,但是服务端解析的时候为了读取 body ,会把下一个协议消息包当成当前 body 给读进去,导致后边所有的包全乱了。

    因此我想解决的是如何比较优雅的处理当前异常消息包而尽量减少对后边消息的影响
    hyq
        9
    hyq  
       2015 年 10 月 21 日
    @broadliyn 客户端自己发错了,怎么能保证接下来的数据没有错误?比如第一个请求是创建一个资源,第二个请求是对资源进行操作。第一个请求出错了,资源没有创建。第二个请求也不应该去执行吧。
    hyq
        10
    hyq  
       2015 年 10 月 21 日
    @broadliyn 错了就应该一巴掌把他拍醒,不要试图去写隐藏错误
    morethansean
        11
    morethansean  
       2015 年 10 月 21 日
    @broadliyn 异常除非别人修改了客户端的代码,然后发一些异常,不然按照你写的程序,严格按照你的协议生成的包,哪来的异常啊?有了那样的异常直接断开链接让重连。
    sujin190
        12
    sujin190  
       2015 年 10 月 21 日
    @broadliyn 这是最稳妥的做法,也不负责,任何头标识都有可能是正常的数据
    wy315700
        13
    wy315700  
       2015 年 10 月 21 日
    加校验
    gamexg
        14
    gamexg  
       2015 年 10 月 21 日
    有 checksum ,识别到了错误就直接关闭连接。
    封装一个打包和解包,所有对 stream 的操作都经过这两个函数,就很难出现错误了。
    xierch
        15
    xierch  
       2015 年 10 月 21 日   2
    …… 请重新学习 TCP
    broadliyn
        16
    broadliyn  
    OP
       2015 年 10 月 21 日
    @xierch 你这样的回答对别人一点帮助也没,反而也是在浪费自己的打字时间
    register
        17
    register  
       2015 年 10 月 21 日   1
    TCP 是流协议,不存在你说的粘包问题。上层定义的 Packet 可能被 TCP 分成多次发送;多个 Packet 也可能被一次发送,这种情况可能就是你说的"粘包"。
    ipconfiger
        18
    ipconfiger  
       2015 年 10 月 21 日
    LZ 说的已经不是粘包的问题了,而是解决客户端写错了给别人擦屁股的问题,无解,解析出错就报错就行了
    inevermore
        19
    inevermore  
       2015 年 10 月 21 日   1
    既然是长连接,那么当客户端传来的报文错误,直接断开就可以,然后让客户端重练就可以
    inevermore
        20
    inevermore  
       2015 年 10 月 21 日   1
    @register 虽然『粘包』是个伪命题,但是一般都这么说。
    qq286735628
        21
    qq286735628  
       2015 年 10 月 21 日 via iPhone
    消息推送? BS 架构可以直接用 SSE ,对于不支持 EventSource 的浏览器,可以很容易用 HTTP 降级处理。
    broadliyn
        22
    broadliyn  
    OP
       2015 年 10 月 21 日
    @register 谢谢,其实主要这里是我没有找到一个合适的说明方式。
    准确的说应该是 tcp 是流协议,其中一个 packet 数据出错可能会影响后续的 packet 数据。。
    broadliyn
        23
    broadliyn  
    OP
       2015 年 10 月 21 日
    @inevermore 非常感谢!
    int64ago
        24
    int64ago  
       2015 年 10 月 22 日 via Android
    每个消息都有固定头尾格式的话,其实一个轮询+正则就搞定了
    wind4
        25
    wind4  
       2015 年 10 月 22 日
    TCP 自带校验,不会出现丢包,也就不会出现解包错误。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3309 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 12:20 PVG 20:20 LAX 04:20 JFK 07:20
    Do have faith in what you're doing.
    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