tcp 半包、粘包问题

2015-10-21 10:47:36 +08:00
 broadliyn

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

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

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

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

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

因此我想解决的是如何比较优雅的处理当前异常消息包而尽量减少对后边消息的影响
hyq
2015-10-21 14:37:30 +08:00
@broadliyn 客户端自己发错了,怎么能保证接下来的数据没有错误?比如第一个请求是创建一个资源,第二个请求是对资源进行操作。第一个请求出错了,资源没有创建。第二个请求也不应该去执行吧。
hyq
2015-10-21 14:38:31 +08:00
@broadliyn 错了就应该一巴掌把他拍醒,不要试图去写隐藏错误
morethansean
2015-10-21 14:40:33 +08:00
@broadliyn 异常除非别人修改了客户端的代码,然后发一些异常,不然按照你写的程序,严格按照你的协议生成的包,哪来的异常啊?有了那样的异常直接断开链接让重连。
sujin190
2015-10-21 15:17:02 +08:00
@broadliyn 这是最稳妥的做法,也不负责,任何头标识都有可能是正常的数据
wy315700
2015-10-21 15:21:08 +08:00
加校验
gamexg
2015-10-21 15:45:04 +08:00
有 checksum ,识别到了错误就直接关闭连接。
封装一个打包和解包,所有对 stream 的操作都经过这两个函数,就很难出现错误了。
xierch
2015-10-21 16:12:34 +08:00
…… 请重新学习 TCP
broadliyn
2015-10-21 17:11:37 +08:00
@xierch 你这样的回答对别人一点帮助也没,反而也是在浪费自己的打字时间
register
2015-10-21 17:30:10 +08:00
TCP 是流协议,不存在你说的粘包问题。上层定义的 Packet 可能被 TCP 分成多次发送;多个 Packet 也可能被一次发送,这种情况可能就是你说的"粘包"。
ipconfiger
2015-10-21 17:49:54 +08:00
LZ 说的已经不是粘包的问题了,而是解决客户端写错了给别人擦屁股的问题,无解,解析出错就报错就行了
inevermore
2015-10-21 20:20:46 +08:00
既然是长连接,那么当客户端传来的报文错误,直接断开就可以,然后让客户端重练就可以
inevermore
2015-10-21 20:21:15 +08:00
@register 虽然『粘包』是个伪命题,但是一般都这么说。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/229769

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX