在使用golang做socket服务时,我想大多数人都会碰见粘包的问题。 以前用python做socket服务时就想写一篇关于tcp粘包的问题,后来因为单纯的tcp服务器开发功能实在烦杂,索性直接用http tornado进行通信了。

下面的资料有些是来自我个人的印象笔记,相关的参考引用链接早就找不到了。


该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。

http://xiaorui.cc/?p=2888

什么是半包 ?

接受方没有接受到完整的包,只接受了一部分。 由于发送方看到内容太大切分数据包进行发送,这样切包能提高传输效率,如果一个包太大,接受方并不能一次接受完。(在长连接和短连接中都会出现)。 

注: 半包、粘包都可以用后面的方法解决.


什么是分包?

既然tcp的包产生了粘包,那么需要分开处理吧。 对,这就是分包 !   分包的前提是客户端和服务端都提前定义一组结构,可以让你准确拆分粘包的结构。 


什么时候需要考虑粘包的问题?

1:  类似 http的请求就不用考虑粘包的问题,因为服务端收到报文后, 就将缓冲区数据接收, 然后关闭连接,这样粘包问题不用考虑到,因为大家都知道是发送一段字符。


2:如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包


3:如果双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构:
    1)”save it” 
    2)”delete it “ 
   这时候很不巧,发送方连续发送这个两个包出去,接收方一次接收可能会是”saveit delete it” 这样接收方就傻了,到底是要干嘛? 不知道,因为协议没有规定这么诡异的字符串,所以要处理把它分包,怎么分也需要双方组织一个比较好的包结构,所以一般可能会在头加一个数据长度之类的包,以确保接收。
 
接着我们用伪代码来实现下tcp粘包的场景.

粘包问题就是TCP在传输数据时, 为了提高传输速度和效率, 把发送缓冲区中的数据拼为一个数据包发送到目的地 比如:

发送方:
send(s, “abce”);
send(s, “decfg”);

接收方:
recv(s, buf); //buf = “abcedecfg”;

再废话下,用一段话来描述什么是tcp粘包:

出现粘包现象的原因既可能由发送方造成,也可能由接收方造成。

1 发送端需要等缓冲区满才发送出去,造成粘包

2 接收方没能及时地接收缓冲区的包,造成多个包接收

解决办法:

为了避免粘包现象,可采取以下几种措施。

1.  对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;

缺点: 第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。

2.  对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;

缺点: 第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。


最后解决tcp粘包的方法:

客户端会定义一个标示,比如数据的前4位是数据的长度,后面才是数据。那么客户端只需发送 ( 数据长度+数据 ) 的格式数据就可以了,接收方根据包头信息里的数据长度读取buffer.


下面直接说golang socket下解决粘包的实例代码.

客户端:

服务端实例代码:

一些经典的问答:


在流传输中出现,UDP不会出现粘包,因为它有消息边界.

如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题. 

END.



对Python及运维开发感兴趣的朋友可以加QQ群 : 478476595 !!!
{ 2000人qq大群内有各厂大牛,常组织线上分享及沙龙,对高性能及分布式场景感兴趣同学欢迎加入该QQ群 }

另外如果大家觉得文章对你有些作用!   帮忙点击广告. 一来能刺激我写博客的欲望,二来好维护云主机的费用.
如果想赏钱,可以用微信扫描下面的二维码. 另外再次标注博客原地址  xiaorui.cc  ……   感谢!
暂无相关产品

发表评论