当前在网络传输应用中,广泛采用的是TCP/IP通信协议及其标准的socket应用开发编程接口(API)。TCP/IP传输层有两个并列的协议:TCP和UDP。其中TCP(transport control protocol,传输控制协议)是面向连接的,提供高可靠性服务。UDP(user datagram protocol,用户数据报协议)是无连接的,提供高效率服务。在实际工程应用中,对可靠性和效率的选择取决于应用的环境和需求。一般情况下,普通数据的网络传输采用高效率的udp,重要数据的网络传输采用高可靠性的TCP。

在应用开发过程中,笔者发现基于TCP网络传输的应用程序有时会出现粘包现象即发送方发送的若干包数据到接收方接收时粘成一包)。针对这种情况,我们进行了专题研究与实验。本文重点分析了TCP网络粘包问题,并结合实验结果提出了解决该问题的对策和方法,供有关工程技术人员参考

一、TCP协议简介

TCP是一个面向连接的传输层协议,虽然TCP不属于iso制定的协议集,但由于其在商业界和工业界的成功应用,它已成为事实上的网络标准,广泛应用于各种网络主机间的通信。

  作为一个面向连接的传输层协议,TCP的目标是为用户提供可靠的端到端连接,保证信息有序无误的传输。它除了提供基本的数据传输功能外,还为保证可靠性采用了数据编号、校验和计算、数据确认等一系列措施。它对传送的每个数据字节都进行编号,并请求接收方回传确认信息(ack)。发送方如果在规定的时间内没有收到数据确认,就重传该数据。数据编号使接收方能够处理数据的失序和重复问题。数据误码问题通过在每个传输的数据段中增加校验和予以解决,接收方在接收到数据后检查校验和,若校验和有误,则丢弃该有误码的数据段,并要求发送方重传。流量控制也是保证可靠性的一个重要措施,若无流控,可能会因接收缓冲区溢出而丢失大量数据,导致许多重传,造成网络拥塞恶性循环。TCP采用可变窗口进行流量控制,由接收方控制发送方发送的数据量。

  TCP为用户提供了高可靠性的网络传输服务,但可靠性保障措施也影响了传输效率。因此,在实际工程应用中,只有关键数据的传输才采用TCP,而普通数据的传输一般采用高效率的udp。

二、粘包问题分析与对策

TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

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

图一

图二

图三

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

  不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。

  在处理定长结构数据的粘包问题时,分包算法比较简单;在处理不定长结构数据的粘包问题时,分包算法就比较复杂。特别是如图3所示的粘包情况,由于一包数据内容被分在了两个连续的接收包中,处理起来难度较大。实际工程应用中应尽量避免出现粘包现象。

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

  • 对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;
  • 对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;
  • 由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

  以上提到的三种措施,都有其不足之处:

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

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

第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

  一种比较周全的对策是:接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。对这种方法我们进行了实验,证明是高效可行的。

三、编程与实现

1.实现框架

  实验网络通信程序采用TCP/IP协议的socket api编程实现。socket是面向客户机/服务器模型的。TCP实现框架如图4所示:

图四

2.主要线程

  编程采用多线程方式,服务器端共有两个线程:发送数据线程、发送统计显示线程。客户端共有三个线程:接收数据线程、接收预处理粘包线程、接收统计显示线程。其中,发送和接收线程优先级设为thread_priority_time_critical(最高优先级),预处理线程优先级为thread_priority_above_normal(高于普通优先级),显示线程优先级为thread_priority_normal(普通优先级)。

  实验发送数据的数据结构如图5所示:

图五

3.分包算法

  针对三种不同的粘包现象,分包算法分别采取了相应的解决办法。其基本思路是首先将待处理的接收数据流(长度设为m)强行转换成预定的结构数据形式,并从中取出结构数据长度字段,即图5中的n,而后根据n计算得到第一包数据长度。

  1)若n<m,则表明数据流包含多包数据,从其头部截取n个字节存入临时缓冲区,剩余部分数据依此继续循环处理,直至结束。

  2)若n=m,则表明数据流内容恰好是一完整结构数据,直接将其存入临时缓冲区即可。

  3)若n>m,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。

  对分包算法具体内容及软件实现有兴趣者,可与作者联系。

四、实验结果分析

  实验结果如下:

  1.在上述实验环境下,当发送方连续发送的若干包数据长度之和小于1500b时,常会出现粘包现象,接收方经预处理线程处理后能正确解开粘在一起的包。若程序中设置了“发送不延迟”:(setsockopt (socket_name,ipproto_tcp,tcp_nodelay,(char *) &on,sizeof on) ,其中on=1),则不存在粘包现象。

  2.当发送数据为每包1kb~2kb的不定长数据时,若发送间隔时间小于10ms,偶尔会出现粘包,接收方经预处理线程处理后能正确解开粘在一起的包。

  3.为测定处理粘包的时间,发送方依次循环发送长度为1.5kb、1.9kb、1.2kb、1.6kb、1.0kb数据,共计1000包。为制造粘包现象,接收线程每次接收前都等待10ms,接收缓冲区设为5000b,结果接收方收到526包数据,其中长度为5000b的有175包。经预处理线程处理可得到1000包正确数据,粘包处理总时间小于1ms。

  实验结果表明,TCP粘包现象确实存在,但可通过接收方的预处理予以解决,而且处理时间非常短(实验中1000包数据总共处理时间不到1ms),几乎不影响应用程序的正常工作。

UNIX网络编程——解决TCP网络传输“粘包”问题的更多相关文章

  1. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    [Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...

  2. 网络编程之tcp协议以及粘包问题

    网络编程tcp协议与socket以及单例的补充 一.单例补充 实现单列的几种方式 #方式一:classmethod # class Singleton: # # __instance = None # ...

  3. 【Linux 网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数:对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...

  4. 【网络编程】TCP网络编程

    TCP网络程序设计 一.模型函数化 图片:TCP编程模型 1.1 创建套接字Socket #include <sys/types.h> /* See NOTES */ #include & ...

  5. python 网络编程(远程执行命令与粘包)

    远程执行命令 先来学习一个新模块 , 一会用到的.. 新模块: subprocess 执行系统命令 r = subprocess.Popen('ls',shell=True,stdout=subpro ...

  6. java网络编程基础——TCP网络编程一

    基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...

  7. 网络编程—代码—TCP网络传输

    一.TCP:用字符流传输信息 ------------------------------------------------------------------------------ [用字符流的 ...

  8. 网络编程socket模块subprocess模块 粘包的解决

    什么是socket? tcp 可靠地面向连接协议 udp 不可靠的,无连接的服务,传送效率高

  9. java网络编程基础——TCP网络编程二

    1.半关闭的Socket 前面的服务器和客户端通信时总是以行为最小数据单位,但是在某些协议里,通信的数据单位可能是多行的,当出现多行数据时就 出现一个问题:Socket输出流如何表示输出数据已经结束. ...

随机推荐

  1. ●BZOJ 1767 [Ceoi2009]harbingers

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1767 题解: 斜率优化DP,单调栈,二分 定义 DP[i] 表示从 i 节点出发,到达根所花 ...

  2. hdu 5391 (数论)

    Zball in Tina Town Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Oth ...

  3. [hdu5608]function

    题意:$\sum_{d|n}f(d)=n^{2}-3n+2$,求$\sum_{i=1}^{n}f(i)\mod 10^{9}+7$ , $n \leqslant 10^{9}$ $\left( T \ ...

  4. win7+Apache 设置域名指向本地文件夹

    实现:浏览器地址栏输入 www.bnzoo.com 访问 D:/www 系统:win7旗舰版+Apache 步骤: 1.打开文件 C:\Windows\System32\drivers\etc\hos ...

  5. MySQL的常用操作命令详解

    系统管理">系统管理 mysql服务">启动MySQL服务 通过windows服务管理器启动MySQL服务 ? 1 开始-->运行-->输入services ...

  6. C语言第三次程序设计作业

    (一)改错题 计算f(x)的值:输入实数x,计算并输出下列分段函数f(x)的值,输出时保留1位小数. 1)源程序(有错误的程序) #include <stdio.h> int main(v ...

  7. 如何在Eclipse中快速添加main方法

    在创建类时自动添加,只需要勾选"public static void main(String[]   args)"

  8. Thread 调用方法的方式

    1.传统ThreadStart 2.ParameterizedThreadStart 3.委托thread = new Thread(delegate() { NoticeBroadcast(user ...

  9. ACM Find them, Catch them

    The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TW ...

  10. Go 语言数据类型

    在 Go 编程语言中,数据类型用于声明函数和变量. 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存. Go 语言按类别有以下几种 ...