我正在阅读有关 TCP 数据流的内容,延迟确认和Nagle 算法。
到目前为止,我的理解是:
- 这延迟确认TCP 上的实现会对收到的段的确认产生延迟,以便应用程序有机会在确认的同时写入一些数据,从而避免发送空的 ACK 数据包并导致网络拥塞。
- 这Nagle 算法实现规定,你不能发送一个小的 TCP 段,而另一个小的段还没有被确认。这避免了流量加载多个微小的。
在某些交互式应用程序(例如 Rlogin)中,Nagle 算法和延迟确认可能会“冲突”:
当我们输入键盘输入时,Rlogin 会将键盘输入发送到服务器,并且某些键(如F1)生成多个字节(F1= Escape + 左括号 + M)。如果这些字节被逐个传送到 TCP,则可以将它们分成不同的段发送。
服务器直到收到整个序列后才会回复回显,因此所有 ACK 都会延迟(等待应用程序发送一些数据)。另一方面,客户端会等待第一个字节确认,然后再发送下一个字节(尊重Nagle 算法)。这种组合最终会导致“滞后”的 Rlogin。
的tcpdump
F1和F2Rlogin 上发送的密钥如下所示:
type Fl key
1 0.0 slip.1023 > vangogh. login: P 1:2(1) ack 2
2 0.250520 (0.2505) vangogh.login > slip.1023: P 2:4(2) ack 2
3 0.251709 (0.0012) slip.1023 > vangogh.login: P 2:4(2) ack 4
4 0.490344 (0.2386) vangogh.login > slip.1023: P 4:6(2) ack 4
5 0.588694 (0.0984) slip.1023 > vangogh.login: . ack 6
type F2 key
6 2.836830 (2.2481) slip.1023 > vangogh.login: P 4:5(1) ack 6
7 3.132388 (0.2956) vangogh.login > slip.1023: P 6:8(2) ack 5
8 3.133573 (0.0012) slip.1023 > vangogh.login: P 5:7(2) ack 8
9 3.370346 (0.2368) vangogh.login > slip.1023: P 8:10(2) ack 7
10 3.388692 (0.0183) slip.1023 > vangogh.login: . ack 10
现在的疑问是:尽管我读到的页面表明服务器在获得整个密钥序列之前不会回复回显,但通过捕获的数据包tcpdump
显示密钥正在其各自的 ACK 上回复(第一个回复长度为 2 个字节,因为来自ESC键是两个字符 - 插入符号 + 左括号)。
如果数据从应用程序发送到 TCP(回显响应),为什么 ACK 会被延迟?据称,关于服务器等待完整序列后再回显,ACK 不是应该不包含任何回声直到最后一个 ACK,而最后一个 ACK 会包含整个序列回声吗?
参考: http://people.na.infn.it/~garufi/didattica/CorsoAcq/Trasp/Lezione9/tcpip_ill/tcp_int.htm
答案1
您说 rlogin“服务器在获得整个序列(例如通常的 F1 键)之前不会回复回显” ^[OP
。但这只是一种毫无根据的假设。而且是错误的假设。您的 tcpdump 实验表明情况并非如此;它恰恰表明 rlogin 实现滞后,没有任何此类“优化”。
事实上,服务器的正常预期行为是立即回应任何输入。如果客户端出于某种原因决定^[
单独发送,那么此后它不会遇到奇怪的延迟((完全不考虑 TCP)。
什么应该解决 laggy-rlogin 类问题的一个干净的方法是,对话的每一方(双方)客户只有send()
当它们真诚地相信此刻有人在等待结果显示时,它们才会发送 。在这种限制下,客户端发送 是一个重大错误^[
,因为软件已经知道这^[OP
是完整的预期序列,而人类用户对完整结果感兴趣,而不仅仅是 的结果^[
(人类是否决定发送OP
或可能OQ
响应,或者什么?)。
因此,对于软件开发人员来说,无论他们使用 TCP 的业务是什么,也无论他们开发客户端还是服务器端,都有反直觉的建议:请记住这send()
不是即时的,它可能会延迟您的下一次传输,因此请更谨慎地使用它(这是因为 Nagle 增加了延迟,而延迟 ACK 会增加更多的延迟)。
答案2
据我所知,Nagle 算法设置在套接字配置的非常低的级别。例如,套接字通信的 C/C++ 实现使用相当低级别的 API 来关闭它。
Nagle 的想法在 TCP/IP 的典型用法中非常好。
例如,我们一直在使用一些软实时系统的消息传递库,并且我们在套接字级别明确将其关闭。正是出于这个原因。我们不想等待。使用 Nagle 的套接字会等待直到整个缓冲区已满 - 无论消息类型如何。因此 ACK 会被延迟,因为它们都很小,缓冲区会慢慢变满。
但实用的提示如下:1. 关闭 Nagle 算法或 2. 如果不能,则发送更大的消息,因此 Nagle 的缓冲区经常是满的。