I.流量控制
TCP使用流量控制来管理数据流量。流量控制限制发送字节大小,防止接收方接收缓存溢出。
1.发送方发送数据速度比接收方处理速度高;这种情况会使接收方缓存溢出,引起数据段被丢弃
2.应用取socket中的缓存数据存在间隔,所以只会偶尔出现缓存为空的情况;比如多媒体数据的接收速度高于播放速度
所以流量控制可以避免因接收方缓存溢出而产生的数据包丢弃,引起不必要的重传
TCP使用滑动窗口机制来实现流量控制
II.滑动窗口机制
i.滑动窗口机制
滑动窗口机制主要用在传输层协议或面向连接协议中,主要完成以下任务:
1.发送多个数据包时,在接收端按发送顺序进行重排(乱序包重排),以保证接收数据顺序与发送数据顺序相同
2.根据数据包的序号,可识别数据包丢失与重复;由数据包的重传机制实现可靠数据传输。
3.根据窗口大小控制TCP数据流量
通过TCP头中以下字段实现上述功能:
1.Sequence Number,对数据进行连续编号,表示数据包第一字节数据编号;
2.Acknowledgment Number,表示已经正确接收的数据编号+1
3.Window,窗口大小,表示还能接收多少数据
下图可以看出滑动窗口怎样实现流量控制:
上图表示快速发送端A向慢速接收端发送数据,窗口大小为4096
1-3:表示握手阶段,协商窗口大小、MSS等信息
4-7:发送4096字节数据,超出发送窗口,等待确认和新窗口
8:确认已经接收的数据
9:通知新窗口
10-15:重复上述情况
16-17:表示挥手阶段
滑动窗口主要信息如下图所示:
发送方
接收方
snd_una:在接收到ack报文后,移动snd_una
snd_wnd:在接收到syn报文或ack报文后,根据window值更新snd_wnd
rcv_wup:发送window值时,更新rcv_wup
rcv_wnd:根据窗口阈值等信息在发送window时,更新rcv_wnd
注意:如果rcv_nxt+新接收数据段长度>rcv_wup+rcv_wnd,会出现数据溢出,进而引起数据包丢弃;直到rcv_wup+rcv_wnd>=rcv_nxt+新接收数据段长度,才能接受新数据
ii.窗口滑动
1.发送窗口滑动
tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_established->tcp_ack->tcp_ack_update_window
更新snd_una,将发送窗口向前移动
2.接收窗口滑动
tcp_sendmsg-> __tcp_push_pending_frames->tcp_write_xmit->tcp_transmit_skb->tcp_select_window
更新rcv_wup,将接收窗口向前移动
iii.发送窗口超出检查:
tcp_sendmsg-> __tcp_push_pending_frames->tcp_write_xmit->tcp_snd_wnd_test
如果当前数据包最后一字节序号>snd_una+snd_wnd,则暂不发送该数据包,以控制发送数据流量
iv.接收窗口超出检查:
快速模式时:
Predicted packet is in window by definition. * seq == rcv_nxt and rcv_wup <= rcv_nxt.所以不用做接收窗口检查
慢速模式时:
tcp_v4_rcv->tcp_v4_do_rcv->tcp_rcv_established->tcp_validate_incoming->tcp_sequence,做接收窗口检查
v.零窗探测
当接收方没有缓存时,会通过ack报文中window=0通知发送方,发送方不能再发送数据。
窗口大小在ack报文中发送,当接收方不向发送方发送数据或接收方的发送窗口为0时,由于发送方的发送窗口为0也不能再发送数据,这样双方就会永远的等待下去。
零窗探测机制主要解决上述问题;通过设置一个零窗探测时钟,当时钟超时时,会发送一个仅有TCP报文头的包到接收方;如果接收方有缓存,就会在ack报文中通知接收方的接收窗口大小。
1.零窗探测时钟处理函数初始化
socket手动创建:
inet_create->tcp_v4_init_sock->tcp_init_xmit_timers->tcp_write_timer->tcp_probe_timer
socket自动创建(listen端):
tcp_v4_rcv->tcp_v4_do_rcv->tcp_v4_hnd_req->tcp_check_req->tcp_v4_syn_recv_sock->tcp_create_openreq_child->tcp_init_xmit_timers->tcp_write_timer->tcp_probe_timer
2.零窗探测时钟启动
tcp_v4_connect->tcp_connect->inet_csk_reset_xmit_timer->sk_reset_timer
3.零窗探测时钟处理函数
tcp_probe_timer->tcp_send_probe0
III.窗口大小
i.窗口大小标度
Window占16位,最大表示值为2^16-1=65535,现在已经完成不了要求,例如一个连接的传输速率为10Mbps,往返时间为100ms,要恒量数据流传输,至少需要10Mbps*100ms=125000 Bytes窗口大小;所以添加了TCP Window-Scaling选项,用来增大窗口上限。
Window-Scaling Option格式为:
Type:3(1Byte)+Len:3(1Byte)+Shift Count(1Byte)
Shift Count最大值限制为14,所以表示的最大窗口值为2^16*2^14=2^30
Window-Scaling Option只能在SYN或SYN-ACK报文中发送,即握手阶段
telnet TCP连接抓包如下,Shift Count一个是6,一个是2:
tcpdump -vvv tcp port 23
18:26:08.187741 IP (tos 0x10, ttl 64, id 5778, offset 0, flags [DF], proto TCP (6), length 60)
192.168.1.29.58542 > 10.20.146.15.telnet: Flags [S], cksum 0xe9b4 (correct), seq 2575510789, win 5840, options [mss 1460,sackOK,TS val 44972951 ecr