TCP通信丢包原因总结 764424567

一、前文 今天在公司问老大,公司的项目底层,是使用的TCP,因为可靠,自动断线重连,在底层都实现了,但是我记得TCP也会有掉包的问题,所以这文章就诞生了——关于TCP掉包的问题,TCP是基于不可靠的网络实现可靠的传输,肯定也会存在掉包的情况。

    如果通信中发现缺少数据或者丢包,那么,最大的可能在于程序发送的过程或者接收的过程出现问题。

    例如服务器给客户端发大量数据,Send的频率很高,那么就有可能在Send时发生错误(原因可能是又多种,可能是程序处理逻辑问题,多线程同步问题,缓冲区溢出问题等等),如果没有对Send失败做处理重发数据,那么客户端收到的数据就会比理论应该收到的少,就会造成丢数据,丢包的现象。

    这种现象,其实本质上来说不是丢包,也不是丢数据,只是因为程序处理有错误,导致有些数据没有成功地被socket发送出去。

二、 原文 原文链接:【TCP通信丢包原因总结】http://www.cnblogs.com/orange1438/p/4693470.html

原文出处:博客园

原文作者:橙&子

二、正文 Socket是使用TCP协议,因为TCP稳定,不会丢失数据,但也是相对而言的。

UDP传输快,适合视频,直播之类的传输数据,但是容易丢失数据;TCP稳定,不会丢失数据,适合传输文字之类的

1.TCP是基于不可靠的网络实现可靠的传输,肯定也会存在掉包的情况

2.TCP是一个“流”协议,一个完整的包可能被TCP拆分成多个包发送,也有可能把小的封装成大的发送,这就是TCP粘包和拆包问题。

3.如果通信中发现缺少数据或者丢包,那么,最大的可能在于程序发送的过程或者接收的过程出现问题。 

例如服务端要给客户端发送大量数据,Send频率很高,那么就很有可能在Send环节出现错误(1.程序处理逻辑错误,2.多线程同步问题,3.缓冲区溢出等),如果没有对Send发送失败做处理,那么客户端收到的数据比理论要收到的数据少,就会造成丢数据,丢包现象。

常用的解决方法有:拆包,加包头,发送组合包,如果是服务器端或者客户端掉线的话,通常采用心跳测试。

心跳测试:每隔一段时间向服务端发送数据包,为了节省资源通常发送空的数据包,如果不能发送成功说明Socket已经断开,这个时候就需要根据具体情况释放资源和重新连接。

粘包、拆包问题说明

假设客户端分别发送数据包D1和D2给服务端,由于服务端一次性读取到的字节数是不确定的,所以可能存在以下4种情况。

1.服务端分2次读取到了两个独立的包,分别是D1,D2,没有粘包和拆包; 2.服务端一次性接收了两个包,D1和D2粘在一起了,被成为TCP粘包; 3.服务端分2次读取到了两个数据包,第一次读取到了完整的D1和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为拆包; 4.服务端分2次读取到了两个数据包,第一次读取到了部分D1,第二次读取D1剩余的部分和完整的D2包; 如果此时服务端TCP接收滑动窗口非常小,而数据包D1和D2都很大,很有可能发送第五种可能,即服务端多次才能把D1和D2接收完全,期间多次发生拆包情况。

接收滑动窗:所谓滑动窗口协议,自己理解有两点:

  1. “窗口”对应的是一段可以被发送者发送的字节序列,其连续的范围称之为“窗口”;

  2. “滑动”则是指这段“允许发送的范围”是可以随着发送的过程而变化的,方式就是按顺序“滑动”

粘包问题的解决策略

由于底层的TCP无法理解上层的业务逻辑,所以在底层是无法确保数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,归纳如下:

1.消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格; 2.在包尾增加回车换行符进行分割,例如FTP协议; 3.将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路是消息头的第一个字段用int来表示消息的总长度;(我之前linux C开发,就用的这种)。 4.更复杂的应用层协议;

764424567wechat 764424567qq 764424567alipay