TCP传输小数据包效率问题(译自MSDN)

http://www.ftpff.com/blog/?q=node/16

摘要:当使用TCP传输小型数据包时,程序的设计是相当重要的。如果在设计方案中不对TCP数据包的
延迟应答,Nagle算法,Winsock缓冲作用引起重视,将会严重影响程序的性能。这篇文章讨论了这些
问题,列举了两个案例,给出了一些传输小数据包的优化设计方案。

背景:当Microsoft TCP栈接收到一个数据包时,会启动一个200毫秒的计时器。当ACK确认数据包
发出之后,计时器会复位,接收到下一个数据包时,会再次启动200毫秒的计时器。为了提升应用程序
在内部网和Internet上的传输性能,Microsoft TCP栈使用了下面的策略来决定在接收到数据包后
什么时候发送ACK确认数据包:
1、如果在200毫秒的计时器超时之前,接收到下一个数据包,则立即发送ACK确认数据包。
2、如果当前恰好有数据包需要发给ACK确认信息的接收端,则把ACK确认信息附带在数据包上立即发送。
3、当计时器超时,ACK确认信息立即发送。
为了避免小数据包拥塞网络,Microsoft TCP栈默认启用了Nagle算法,这个算法能够将应用程序多次
调用Send发送的数据拼接起来,当收到前一个数据包的ACK确认信息时,一起发送出去。下面是Nagle
算法的例外情况:
1、如果Microsoft TCP栈拼接起来的数据包超过了MTU值,这个数据会立即发送,而不等待前一个数据
包的ACK确认信息。在以太网中,TCP的MTU(Maximum Transmission Unit)值是1460字节。
2、如果设置了TCP_NODELAY选项,就会禁用Nagle算法,应用程序调用Send发送的数据包会立即被
投递到网络,而没有延迟。
为了在应用层优化性能,Winsock把应用程序调用Send发送的数据从应用程序的缓冲区复制到Winsock
内核缓冲区。Microsoft TCP栈利用类似Nagle算法的方法,决定什么时候才实际地把数据投递到网络。
内核缓冲区的默认大小是8K,使用SO_SNDBUF选项,可以改变Winsock内核缓冲区的大小。如果有必要的话,
Winsock能缓冲大于SO_SNDBUF缓冲区大小的数据。在绝大多数情况下,应用程序完成Send调用仅仅表明数据
被复制到了Winsock内核缓冲区,并不能说明数据就实际地被投递到了网络上。唯一一种例外的情况是:
通过设置SO_SNDBUT为0禁用了Winsock内核缓冲区。

Winsock使用下面的规则来向应用程序表明一个Send调用的完成:
1、如果socket仍然在SO_SNDBUF限额内,Winsock复制应用程序要发送的数据到内核缓冲区,完成Send调用。
2、如果Socket超过了SO_SNDBUF限额并且先前只有一个被缓冲的发送数据在内核缓冲区,Winsock复制要发送
的数据到内核缓冲区,完成Send调用。
3、如果Socket超过了SO_SNDBUF限额并且内核缓冲区有不只一个被缓冲的发送数据,Winsock复制要发送的数据
到内核缓冲区,然后投递数据到网络,直到Socket降到SO_SNDBUF限额内或者只剩余一个要发送的数据,才
完成Send调用。

案例1
一个Winsock TCP客户端需要发送10000个记录到Winsock TCP服务端,保存到数据库。记录大小从20字节到100
字节不等。对于简单的应用程序逻辑,可能的设计方案如下:
1、客户端以阻塞方式发送,服务端以阻塞方式接收。
2、客户端设置SO_SNDBUF为0,禁用Nagle算法,让每个数据包单独的发送。
3、服务端在一个循环中调用Recv接收数据包。给Recv传递200字节的缓冲区以便让每个记录在一次Recv调用中
被获取到。

性能:
在测试中发现,客户端每秒只能发送5条数据到服务段,总共10000条记录,976K字节左右,用了半个多小时
才全部传到服务器。

分析:
因为客户端没有设置TCP_NODELAY选项,Nagle算法强制TCP栈在发送数据包之前等待前一个数据包的ACK确认
信息。然而,客户端设置SO_SNDBUF为0,禁用了内核缓冲区。因此,10000个Send调用只能一个数据包一个数据
包的发送和确认,由于下列原因,每个ACK确认信息被延迟200毫秒:
1、当服务器获取到一个数据包,启动一个200毫秒的计时器。
2、服务端不需要向客户端发送任何数据,所以,ACK确认信息不能被发回的数据包顺路携带。
3、客户端在没有收到前一个数据包的确认信息前,不能发送数据包。
4、服务端的计时器超时后,ACK确认信息被发送到客户端。

如何提高性能:
在这个设计中存在两个问题。第一,存在延时问题。客户端需要能够在200毫秒内发送两个数据包到服务端。
因为客户端默认情况下使用Nagle算法,应该使用默认的内核缓冲区,不应该设置SO_SNDBUF为0。一旦TCP
栈拼接起来的数据包超过MTU值,这个数据包会立即被发送,不用等待前一个ACK确认信息。第二,这个设计
方案对每一个如此小的的数据包都调用一次Send。发送这么小的数据包是不很有效率的。在这种情况下,应该
把每个记录补充到100字节并且每次调用Send发送80个记录。为了让服务端知道一次总共发送了多少个记录,
客户端可以在记录前面带一个头信息。

案例二:
一个Winsock TCP客户端程序打开两个连接和一个提供股票报价服务的Winsock TCP服务端通信。第一个连接
作为命令通道用来传输股票编号到服务端。第二个连接作为数据通道用来接收股票报价。两个连接被建立后,
客户端通过命令通道发送股票编号到服务端,然后在数据通道上等待返回的股票报价信息。客户端在接收到第一
个股票报价信息后发送下一个股票编号请求到服务端。客户端和服务端都没有设置SO_SNDBUF和TCP_NODELAY
选项。

性能:
测试中发现,客户端每秒只能获取到5条报价信息。

分析:

这个设计方案一次只允许获取一条股票信息。第一个股票编号信息通过命令通道发送到服务端,立即接收到
服务端通过数据通道返回的股票报价信息。然后,客户端立即发送第二条请求信息,send调用立即返回,
发送的数据被复制到内核缓冲区。然而,TCP栈不能立即投递这个数据包到网络,因为没有收到前一个数据包的
ACK确认信息。200毫秒后,服务端的计时器超时,第一个请求数据包的ACK确认信息被发送回客户端,客户端
的第二个请求包才被投递到网络。第二个请求的报价信息立即从数据通道返回到客户端,因为此时,客户端的
计时器已经超时,第一个报价信息的ACK确认信息已经被发送到服务端。这个过程循环发生。

如何提高性能:
在这里,两个连接的设计是没有必要的。如果使用一个连接来请求和接收报价信息,股票请求的ACK确认信息会
被返回的报价信息立即顺路携带回来。要进一步的提高性能,客户端应该一次调用Send发送多个股票请求,服务端
一次返回多个报价信息。如果由于某些特殊原因必须要使用两个单向的连接,客户端和服务端都应该设置TCP_NODELAY
选项,让小数据包立即发送而不用等待前一个数据包的ACK确认信息。

提高性能的建议:
上面两个案例说明了一些最坏的情况。当设计一个方案解决大量的小数据包发送和接收时,应该遵循以下的建议:
1、如果数据片段不需要紧急传输的话,应用程序应该将他们拼接成更大的数据块,再调用Send。因为发送缓冲区
很可能被复制到内核缓冲区,所以缓冲区不应该太大,通常比8K小一点点是很有效率的。只要Winsock内核缓冲区
得到一个大于MTU值的数据块,就会发送若干个数据包,剩下最后一个数据包。发送方除了最后一个数据包,都不会
被200毫秒的计时器触发。
2、如果可能的话,避免单向的Socket数据流接连。
3、不要设置SO_SNDBUF为0,除非想确保数据包在调用Send完成之后立即被投递到网络。事实上,8K的缓冲区适合大多数
情况,不需要重新改变,除非新设置的缓冲区经过测试的确比默认大小更高效。
4、如果数据传输不用保证可靠性,使用UDP。

TCP传输小数据包效率问题(译自MSDN)的更多相关文章

  1. [转]SOCKET通信中TCP、UDP数据包大小的确定

    TCP.UDP数据包大小的确定 UDP和TCP协议利用端口号实现多项应用同时发送和接收数据.数据通过源端口发送出去,通过目标端口接收.有的网络应用只能使用预留或注册的静态端口:而另外一些网络应用则可以 ...

  2. [转]TCP、UDP数据包大小的确定

       TCP.UDP数据包大小的确定   http://blog.163.com/jianlizhao%40126/blog/static/1732511632013410101827640/   U ...

  3. TCP、UDP数据包大小的限制(UDP数据包一次发送多大为好)——数据帧的物理特性决定的,每层都有一个自己的数据头,层层递减

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

  4. TCP、UDP数据包大小的限制

    版权声明:本文为灿哥哥http://blog.csdn.net/caoshangpa 原创文章,转载请标明出处. https://blog.csdn.net/caoshangpa/article/de ...

  5. 【转】TCP、UDP数据包大小的限制

    来自:https://blog.csdn.net/caoshangpa/article/details/51530685 1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层 ...

  6. TCP和UDP数据包大小限制

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

  7. TCP,UDP,IP数据包的大小限制

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

  8. TCP/IP协议数据包文件PCAP分析器

    一.设计原理 1.PCAP文件构成 参考http://blog.csdn.net/gulu_gulu_jp/article/details/50494909 PCAP文件由一个PCAP文件头和多个PC ...

  9. 【VS开发】【DSP开发】TCP和UDP数据包结构

    TCP (Transport Control Protocol)传输控制协议: 1.TCP数据包的分组格式: A,源端口:标识源端应用进程. B, 目的端口:标识目的端应用进程. C, 序号:在SYN ...

随机推荐

  1. win32进阶之路:给锁屏软件增加一个超链接按钮

    前言: windows下一切皆窗口,我们看到的超链接也是窗口,效果图如下: 鼠标放在赵大哥博客园主页和关于软件两个按钮上,按钮上的字体会从绿色变成红色,同时鼠标指针变为手型,点下鼠标左键就会调用默认浏 ...

  2. 序列化与反序列化Serialize&Deserialize

    序列化是指一个对象的实例可以被保存,保存成一个二进制串,当然,一旦被保存成二进制串,那么也可以保存成文本串了.比如,一个计数器,数值为2,我们可以用字符串“2”表示.如果有个对象,叫做connter, ...

  3. Samba Linux和Windows互访

    之前一直用的sshfs manager,但是会出现造成电脑黑屏的情况.网上也没找到其它类似的软件. 后面打算用Samba 直接搜到一篇很强大的文章. 1.  下载及安装Samba 推荐用yum来安装, ...

  4. 增强的for循环(或foreach)

    增强的for循环(也称为foreach循环):不用下标变量,顺序的訪问整个数组.不能以其它顺序訪问数组,或者改变数组的元素. for(elementType element: arrayRefVar) ...

  5. 【Android开发经验】使用Ant批量打包Android应用全然指南

    本文章由Socks完毕.博客地址:http://blog.csdn.net/zhaokaiqiang1992 转载请说明. 折腾了一下午.百度了一下午,最终实现了使用Ant对Android应用的批量打 ...

  6. paip.mysql 5.6 安装总结

    paip.mysql 5.6 安装总结 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn.net/atti ...

  7. MySQL查询in操作 查询结果按in集合顺序显示(转)

    MySQL 查询in操作,查询结果按in集合顺序显示的实现代码,需要的朋友可以参考下. MySQL 查询in操作,查询结果按in集合顺序显示 复制代码代码如下: select * from test ...

  8. WCF的ABC

    首先: WCF的全称是Windows Communication Foundation,Windows通信基础的意思,是Microsoft为构建面向服务的应用程序而提供的统一编程模型,它整合.NET平 ...

  9. Asp.Net 之 调用远程Web_Service

    一.添加web service引用 1.右键 Web 项目 → “添加服务引用”: 2.右键已有的 App_WebReferences 文件夹 → “添加服务引用”: 二.引用远程web servic ...

  10. Android之网络编程利用PHP操作MySql插入数据(四)

    因为最近在更新我的项目,就想着把自己在项目中用到的一些的简单的与网络交互的方法总结一下,所以最近Android网络编程方面的博文会比较多一些,我尽量以最简单的方法给大家分享,让大家明白易懂.如果有什么 ...