设计问题-通过使用 Winsock TCP 发送较小的数据段

重要说明:本文是由 Microsoft 机器翻译软件进行的翻译并可能由 Microsoft 社区通过社区翻译机构(CTF)技术进行后期编辑,或可能是由人工进行的翻译。Microsoft 同时向您提供机器翻译、人工翻译及社区后期编辑的文章,以便对我们知识库中的所有文章以多种语言提供访问。翻译的文章可能存在词汇、句法和/或语法方面的错误。Microsoft 对由于内容的误译或客户对内容的使用所导致的任何不准确、错误或损失不承担责任。

点击这里察看该文章的英文版: 214397
概要
您需要通过 TCP 发送较小的数据的数据包,Winsock 应用程序设计时,尤为重要。不会不考虑到延迟的确认,Nagle 算法以及 Winsock 缓冲的交互设计可以显著影响性能。这篇文章将讨论这些问题,使用了几个案例研究,并派生的 Winsock 应用程序从高效地发送较小的数据的数据包的建议一系列。
更多信息

背景

当 Microsoft TCP 堆栈接收数据包时,200 毫秒延迟计时器响起。当最终发送一个 ACK 时,延迟计时器重置,并且接收到下一个数据包时,将启动另一个 200 毫秒的延迟。提高在互联网和内联网应用程序的效率,Microsoft TCP 堆栈使用以下标准来决定何时发送一个 ACK 数据包接收到的数据:
  • 如果在延迟计时器过期之前收到第二个数据包,将确认发送。
  • 如果有数据要发送 ACK 的方向相同,收到第二个数据包,并在延迟计时器过期之前,确认是 piggybacked 与数据段并立即发送。
  • 当延迟计时器到期时,将确认发送。
若要避免 congest 网络的小型数据包,Microsoft TCP 堆栈时,可使 Nagle 算法,默认情况下,将合并来自多个发送调用和延迟直到上一个数据包的 ACK 发送接收从远程主机发送的较小的数据缓冲区。Nagle 算法的两个例外情况如下:
  • 如果堆栈包含合并数据缓冲区大比最大传输单位 (MTU),一个全尺寸的数据包而无需等待来自远程主机的确认立即发送。在以太网上,tcp/ip 的 MTU 为 1460 字节。
  • 应用 TCP_NODELAY 套接字选项,以禁用 Nagle 算法,使较小的数据的数据包会传输到远程的主机,而不会延迟。
若要优化性能,在应用程序层,Winsock 副本从应用程序的数据缓冲区发送到 Winsock 内核缓冲区的调用。然后,堆栈使用自己 (如 Nagle 算法) 的试探法确定何时把数据包在网络上。您可以更改分配给使用 SO_SNDBUF 选项 (它是 8k,默认情况下) 的套接字的 Winsock 内核缓冲区的数量。如有必要,Winsock 可以极大地缓冲超过 SO_SNDBUF 缓冲区大小。在大多数情况下,在应用程序完成发送只表示应用程序中的数据缓冲区发送呼叫将被复制到的 Winsock 内核缓冲区,并不表示数据已达到网络媒体。唯一的例外是当您禁用了 Winsock 缓冲通过将 SO_SNDBUF 设置为 0。

Winsock 使用下列规则来指示应用程序完成发送 (具体取决于调用发送方式,完成通知可能是从一个阻塞调用,发送信号事件或调用通知函数,返回的函数等):
  • 如果仍在 SO_SNDBUF 配额是套接字,Winsock 复制从应用程序发送的数据,并指示完成发送给应用程序。
  • 如果没有堆栈内核缓冲区中仍然只有一个以前缓冲的发送套接字是 SO_SNDBUF 配额之外,Winsock 复制从应用程序发送的数据,并指示完成发送给应用程序。
  • 如果套接字已超出 SO_SNDBUF 配额,没有堆栈内核缓冲区中发送的多个以前缓冲,Winsock 会复制从应用程序发送的数据。堆栈完成足够发送放回在 SO_SNDBUF 配额或只有一个未完成发送条件内的套接字之前,Winsock 并不表示完成发送给应用程序。

案例研究 1

概述:

Winsock TCP 客户端需要将 10000 的记录发送到 Winsock TCP 服务器,存储在数据库中。记录的大小为 100 字节长因 20 个字节。为了简化应用程序逻辑,设计,是,如下所示:
  • 客户端执行仅阻止发送。该服务器将执行仅阻止接收。
  • 客户端套接字将 SO_SNDBUF 设置为 0,这样,每个记录中的单个数据段熄灭。
  • 服务器在循环中调用接收。发布在接收的缓冲区是 200 个字节,因此可能会收到一个接收调用中的每个记录。

性能:

在测试期间,开发人员查找客户端可以只发送到服务器的每秒五条记录。总 10000 记录最大值在 976 64k 字节的数据 (10000 * 100 / 1024年),采用多个半小时将发送到服务器。

分析:

由于客户端未设置 TCP_NODELAY 选项,Nagle 算法会强制 TCP 堆栈来等待一个 ACK 之前可以将另一个数据包发送线路上。但是,客户端已禁用通过 SO_SNDBUF 选项设置为 0 的 Winsock 缓冲。因此,10000 发送调用必须发送确认,并分别。每个确认是 200 毫秒的延迟,因为服务器的 TCP 堆栈上发生以下情况:
  • 当服务器收到一个数据包时,其 200 毫秒延迟计时器响起。
  • 服务器不需要发送任何内容,因此不能 piggybacked ACK。
  • 客户端将不会发送另一个数据包,除非得到确认前一个包。
  • 在服务器上的延迟计时器过期并将确认发送回。

如何改进:

有此设计两个方面的问题。首先,没有延迟计时器问题。客户端需要能够向在 200 毫秒内服务器发送两个数据包,因为默认情况下,客户端使用 Nagle 算法,应该只使用默认的 Winsock 缓冲,并将 SO_SNDBUF 设置为 0。TCP 堆栈已合并比最大传输单位 (MTU) 更大的缓冲区,一旦被立即发送全大小的数据包,而无需等待来自远程主机的确认。

其次,这种设计为每个记录的这种小尺寸调用一个发送。发送此大小的小不是非常有效的。在这种情况下,开发人员可能想要填充到 100 个字节的每个记录,并发送 80 记录从一个客户端一次发送呼叫。要让服务器知道将总共发送多少条记录,客户端可能想要从通信包含数的记录,请按照修补程序大小的标头的开始。

案例研究 2

概述:

Winsock TCP 客户端应用程序打开的 Winsock TCP 服务器应用程序提供股票报价服务的两个连接。第一个连接命令通道用于向服务器发送股票。第二个连接作为数据信道用于接收股票报价。已建立两个连接后,客户端向服务器通过命令通道发送股票代码和等待要回来通过数据通道的股票。只在收到第一个股票报价之后,它会向服务器发送下一个股票代码请求。在客户端和服务器未设置 SO_SNDBUF 和 TCP_NODELAY 选项。

性能:

在测试期间,开发人员查找客户端仅能获得每秒五个报价。

分析:

这种设计会一次只允许一个出色的股票报价请求。第一个股票发送通过命令通道 (连接) 到服务器,并且响应是立即重新从服务器发送到客户端通过数据通道 (连接)。然后,客户端立即发送第二个股票代码请求发送到 Winsock 内核缓冲区复制请求缓冲区发送调用中的时立即返回。但是,客户端的 TCP 堆栈不能发送请求从其内核缓冲区立即因为通过发送第一个命令通道未尚未确认。200 毫秒延迟计时器后服务器命令通道过期,则第一个符号请求的确认会回到客户端。然后,第二个报价请求成功地发送到服务器后延迟为 200-女士的报价单的第二个股票恢复后立即通过数据通道,因为此时,在客户端数据信道延迟计时器已过期。以前的报价响应的 ACK 是由服务器接收的。(请记住客户端无法发送第二个股票报价请求的 200 毫秒,从而使为延迟计时器过期并向服务器发送一个 ACK 客户端上的时间)。因此,客户端获取的第二个报价单响应,可以发出另一个报价请求,遵守相同的周期。

如何改进:

两个连接 (通道) 设计此处是不必要的。如果只有一个连接用于股票报价请求和响应,可以在报价单响应 piggybacked 报价请求的确认和立即回来。为了进一步提高性能,客户端可以"多路传输"股票报价的多个请求到一个发送呼叫的服务器和服务器可能还"多路传输"多个报价单响应到一个发送到客户端的调用。如果由于某种原因确实需要两个单向通道设计,双方应设置 TCP_NODELAY 选项,以便无需等待前一个数据包的 ACK,可以立即发送小数据包。

建议:

而在编造这些两个案例研究,它们有助于阐明某些最坏的情况。在设计应用程序,其中涉及到大量的小型数据段发送和 recvs,您应考虑以下准则:
  • 如果的数据段不急于,应用程序应合并它们到一个更大的数据块,要传递到发送调用。因为可能会被复制到 Winsock 内核缓冲区发送缓冲区,该缓冲区不应太大。略少于 8k 时通常是有效。只要 Winsock 内核获取块大于 MTU,它将发送出多个大包和剩余的最后一个数据包。发送端,除最后一个数据包中,不会命中由 200 毫秒延迟计时器。最后一个数据包,如果它正好是一个奇数据包,则仍可能会有延迟的确认算法。如果发送端堆栈获取另一个块大于 MTU,它仍然可以绕过 Nagle 算法。
  • 如果可能,请避免使用单向数据流的套接字连接。通过单向套接字的通信,这是更容易受到 Nagle 和延迟确认算法。如果通信遵循请求和响应流,则应使用单个插座进行发送和 recvs,以便可以在响应中 piggybacked ACK。
  • 如果要立即发送所有较小的数据段,则将在发送端的 TCP_NODELAY 选项的设置。
  • 除非您想要保证网络上发送一个数据包,当发送完成由 Winsock,应不将 SO_SNDBUF 设置为零。实际上,默认 8k 缓冲区已试探性地确定适用于大多数情况下,您不能更改它除非进行了测试您新的 Winsock 缓冲区设置使您可以更好的性能,而不是默认。此外,将 SO_SNDBUF 设置为零很是有用的应用程序执行批量数据传输的。甚至,实现最大效率使用时应结合双缓冲 (在任何给定的时间多个未完成发送) 然后重叠 I/O。
  • 如果不需要得到保证,数据传递,则使用 UDP。
参考
有关延迟确认和 Nagle 算法的详细信息,请参阅:

Braden,R.[1989],RFC 1122,要求互联网主机的通讯层,互联网工程任务组。

警告:本文已自动翻译

属性

文章 ID:214397 - 上次审阅时间:03/14/2015 10:13:00 - 修订版本: 4.0

  • kbdswnet2003swept kbapi kbinfo kbip kbnetwork kbwinsock kbmt KB214397 KbMtzh
反馈