DIOCP之DEMO-粘包问题及解决

什么是粘包问题?

举个例子:sever发送20480 个A字母,可是由于client一次只收到10240个A,余下的又发送一次:第一个包中包括有此数据包的总长度,读取出长度,然后接收一个对比长度,如果当前长度<包标定长度,那么就等余下包,一直等到足够包长度,把他们组合在一起就算接收一个数据包.

TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。    
出现粘包现象的原因是多方面的,它既可能由发送方造成,也可能由接收方造成。发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。

粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包,此处假设用户接收缓冲区长度为m个字节。

不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。    
在处理定长结构数据的粘包问题时,分包算法比较简单;在处理不定长结构数据的粘包问题时,分包算法就比较复杂。特别是如图3所示的粘包情况,由于一包数据内容被分在了两个连续的接收包中,处理起来难度较大。实际工程应用中应尽量避免出现粘包现象。    
为了避免粘包现象,可采取以下几种措施。一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。    
以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。    
一种比较周全的对策是:接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。对这种方法我们进行了实验,证明是高效可行的。

解决方案:TLV协议

TLV协议说明:
T(Tag)字段表示数据包类型,传输中定义为short类型;L(length)字段表示数据包长度(不包括类型和长度字段本身),传输中定义为long类型.T和L共同组成包头;V(value)表示数据包信息。

TSendPacket=record //
   PType:Word; //包类型
   PLength:DWORD; //长度
   VehicleInfo:TVehiclePacket; //数据
   end;

 

转载于:https://www.cnblogs.com/diocp/p/5837984.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Q: 在Diocp中我需要另外开线程去处理逻辑吗? A: 在编码层(TDiocpCoderTcpServer), 逻辑处理事件(OnContextAction), 是由DiocpTask/Qworker(编译开关(QDAC_QWorker)可以进行切换)驱动的,和底层的通信线程是不相干扰。所以在编码层不需要使用另外的线程池,来处理逻辑。 Q: 同一个连接的OnContextAction会同时被多个线程触发吗? A:编码层的OnContextAction是排队处理的,底层接收到数据后交由注册的解码器进行解码, 解码成功后,放到任务队列,然后依次触发OnContextAction,所以同一个连接的OnContextAction同时只会有一个线程调用。 Q: Diocp的断开日志 A: Diocp在DEBUG模式下面会记录详细的日志。会记录每个连接断开的原因。如下 1> xxxx:[2824]投递发送数据请求时出现了错误。错误代码:10054 说明: 这种日志在发送数据的时候系统返回了错误代码,可以根据错误代码(如10054),查询到相应的错误信息。 2> xxxx: [720]响应接收请求时出现了错误。错误代码:64! 说明:这种日志在响应接收请求时系统返回的错误,根据错误代码查询响应的信息。 3> xxxx: [704]接收到0字节的数据,该连接将断开! 说明: 服务端接收到长度为0字节的数据,认为对方关闭了连接。服务端相应的会释放连接触发OnDiscconected函数。 4> xxxx :[812]执行[CheckNextSendRequest::lvRequest.ExecuteSend]失败: 处理投递发送请求数据包时,发现异步关闭请求(Request.Tag = -1)。进行关闭处理! 说明: 这种日志一般在服务端投递了异步关闭请求(PostWSAClose)时,会出现该日志。 上面列举的日志都是正常的,DIOCP只是记录详细情况,以便出现问题时能有据可查,请勿惊慌。 * 中括号[]中的数字是连接对应的SocketHandle,是连接的套接字句柄。 MSDN上面错误代码说明: https://msdn.microsoft.com/en-us/library/ms740668.aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值