网络中的TCP数据流可以分成两类:块数据(邮件发送接收、文件传输、媒体数据等)和交互数据(telnet、ssh等),若按分组数量计算则块数据和流数据在网络中所占比例相同。TCP对两种流的处理方式不同,本文讨论对于交互数据的处理方式。
一、交互输入
当对一个telnet连接进行抓包时,会发现每一个按键都会产生一个41字节长度的数据分组(20字节IP首部+20字节TCP首部+1字节数据),并且服务器回显也要生成一个分组,这样用户键入一个字符会在连接上产生4个报文:客户端交互按键、服务器对按键的确认、服务器对按键的回显、客户端对按键回显的确认。本例实验中使用Ubuntu 16.04连接一台H3C S5130交换机,软件版本为Comware 7.1.045,如下图为登录过程中输入用户名的结果(用户名:admin,密码:passwd)。
前四个数据包为输入用户名第一个字符“a“的交互过程,第一、三个为输入和回显,第二、四个为对输入和回显的分别确认。整个登录过程共要交互48个数据包,包含输入”回车“符时交换机发出的协议选项字段。
登录交互过程效果如下:
登录完成后交换机将返回输入提示符,并触发一次客户端的确认:
登录成功后在交互终端中输入 “display version“ 命令查看交换机版本信息,此时命令执行结果将使用一个数据包全部返回(不考虑分片情况),每个字段使用”\r”或”\r\n”间隔,结果如下:
追踪流将更直观的表现一次交互的过程:
telnet交互流是一个非常简单的过程,但对I/O资源利用率偏高且有较多必要性不高的操作,如一次按键的第2、3个数据包可以合并为一个报文发送,同时携带对按键输入的确认和服务器的回显,减少一次I/O操作,这将使用“Delayed ACK”技术。
二、Delayed ACK
从上述实验中可以看出,TCP交互流中会产生大量多余的ACK极大消耗IO资源,以telnet为例,可以将单次交互中的第2、3个报文合并成一个发送,这就是TCP的Delayed ACK(延迟确认)功能。延迟确认的机制如下:
①TCP kernel维护一个默认为200ms的定时器,在TCP连接建立后启动。
②若定时器内应用层有需要发送的数据(即需要对该报文做出应用层实时响应),则该ACK会携带响应报文的payload立刻发送。
③若定时器超时应用层无响应,就只发送空的ACK报文(无payload)。
④注意该定时器不是在收到一个需要确认的报文后启动,而是TCP内核每隔200ms检查一次是否有需要发送的ACK并进行发送。例如数据段在第150ms到达,那么ack最晚会在第200ms发出,而不是第350ms。
⑤定时器时间可自定义,但不能大于500ms。
开启延迟确认机制的TCP连接可以极大降低I/O次数,但在某些场景下会引入大量多余延时。想象如下场景:某客户端间歇向服务器发送报文且该报文不需要应用层响应,那么服务器也需要等待定时器超时后回应ack,若该报文每隔200ms发送一次共发送10次,那么最坏情况下(刚好在定时器超时后发送且窗口较小)发送并确认这10个报文将引入额外2000ms延迟,这对于传输层协议来说是不可接受的。
三、Nagle算法
RFC-896中建议了一种称为Nagle的自适应算法,该算法要求一个TCP连接上最多只能有一个未被确认的未完成的小分组,在该分组的确认到达之前不能发送其他的小分组,TCP会收集这些不能发送的分组并在收到确认后以一个分组的方式发送出去。即TCP会合并一定数量的分组一次送出。该算法的描述如下:
while True:
if有报文需要发送:
if窗口大小>= MSS &&可发送报文>= MSS && FIN未置位:
立即发送该报文
else:
if连接中有未确认的报文:
在下一个ACK到达之前将报文加入缓冲区
else:
MSS=最大报文大小
该算法可以有效减少交互应用连接小段报文的数量,且这个算法的优越性在于它是自适应的:确认到达的越快,数据也就发送的越快,而在低速广域网链路上则会发送更少的分组。而如果是块数据传输,Nagle算法将无谓引入过多延迟,因此建议在块传输应用上关闭该算法。且此算法和DelayedACK兼容性极差,不能在链路两端启用两种算法。
本文简述了TCP处理交互数据流的方法,下篇文章将对Nagle和Delayed-ACK共用的场景进行算法模拟,并讨论块数据流的处理方法。
领取专属 10元无门槛券
私享最新 技术干货