今天对网络数据抓包!查了一下TCP的资料!觉得对大家可能有用就放在这里吧!
TCP 的包头
SEQ
等于该主机选择本次连接的初始序号加上报文段中第一个字节在整个数据流中的序号。在连接建立的时候,会随机选择一个初始序号,如果发送的数据包中的字节是整个数据流中的第 256 字节到 512 字节。
WINDOW
16 个字节,接受方用来通知发送方,我的接受 buffer 的大小,发送方不能过分的发送,导致接受方的 buffer 溢出。 单位是字节。
SYN
建立连接用的。
ACK
建立连接和数据发送,关闭连接都用。
RST
连接异常。
FIN
关闭连接。
PSH
表示接受方应该尽快把数据发送给应用程序, 现在的 TCP 模块一般都可以自动识别处理 PSH 。
URG
发送带外数据。
TCP 的重传机制 Go-Back-N
接受方不断发送 ACK 信息和 32 位的 acknowledge seq number, 用来通知发送方,期望接收到数据包的序号,同时也表示已经正确接收的数据。acknowledge
seq number 不一定要连续。
Delayed ACK
TCP 并不是对收到的数据立即发送 ACK 信息,而是希望
ACK 信息随着数据一起发送。
如果在 200 ms 内,没有数据需要发送,那么就发送一个不含数据的 ACK 数据包。
这个数据包不应该增加 TCP 的顺序编号,接受方应该从数据长度上判断出这个包是一个控制包。
还有一种算法是如果接收到一个数据包之后,启动一个计时器,在计时器超时之前没有可供 piggy back 的数据发送,那么就发送一个 pure ACK ,一个不带数据包的 ACK 消息。
Nagle 算法
要解决得的问题
只传一个字节的数据,需要带上 20 bytes 的 IP
header, 20 bytes 的 TCP header , 效率很低。
解决办法
让 TCP 的连接只允许一个 TCP 的小包发送出去而没有得到确认。也就是说,如果发送了一个小包出去,
那么在没有收到确认消息之前,不能在发送新的小数据包。
分析
如果往返时间很短的话,在下一个小数据包到来之前,
就收到了对于上一个这个数据包的确认信息,所以在这种情况下, Nagle 算法不会起作用。字节的数据在以太网上的 RTT 大约是 16 ms 。
如果往返时间很长的话,在发送下一个小数据包的时候,还没有接收到上一个小数据包的确认信息,那么就算拥塞算法允许发送数据包,也不可以发送,这就是 Nagle 算法,这样就可以把很多小的数据积累起来,
在一个数据包中一起发送。尤其适用于 Rlogin 等交互式服务。
滑动窗口
当收到 ACK 的时候,发送方的窗口变大,也叫开窗, 当发送数据的时候,发送方的窗口变小,也叫关窗。
在窗子完全关闭的时候,不能在发送数据了。
慢启动
cwnd ,表示拥塞窗口的大小,单位是字节。初始化的时候, cwnd 是一个数据段的大小。cwnd 的增加是一个数据段的长度。
发送方收到一个 ACK 那么就增加 cwnd 。
cwnd = cwnd + MSS
cwnd = min(cwnd,awnd)
问题, 如果是 DUPACK ,那么也应该增加。
问题, 一个 segment 的大小是可变的,假设是个固定值
当出现 3 个 DUPACK 的时候,进入 Fast Recovery 过程。
???当出现超时重传,则进到 Congestion Avoid 的过程。
当 cwnd 大于 ssthrd ,那么进入 Congestion Avoid 的过程。
Congestion Avoid
收到一个 ACK 的时候,增加 cwnd。
cwnd = cwnd + MSS * MSS / cwnd
cwnd = min(cwnd,awnd)
如果有 3 各重复的 ACK ,那么应该进入 Fast
Recovery 的过程。
如果有重传,那么应该进入 Slow Start 的过程。可以重传第一个 Unacknowledged segment ,也可以重传许多。
DUP ACK 不能增加
Fast Recovery
当收到 3 各 DUPACK 的时候会进入 Fast Recovery .
重传第一个 unacknowledged segment.
改变 ssthres , sshres = max(flightsize/2, 2*MSS)
改变 cwnd , cwnd = sshres + 3
重置重传定时器。
如果还有新的 DUPACK 到来,那么 cwnd 还会增加, 每次增加 MSS, 当有 ACK 确认最新的数据,那么 cwnd 设置成为 sshres , 进入 Congestion Avoid 的过程。
问题,如果在 fast Recovery 的过程中,出现重传, 应该进入 Slow Start 的过程。
发生重传的处理
cwnd = MSS
sshres = max(flightsize/2, 2*MSS)
重置对于 DUPACK 的计数器
RTO = min(2*RTO, 64 seconds)
如果一个包超过重传 4 次,那么 TCP 连接关闭。
重置关于重传的计时器。
停止测量 RTT 和 RTO 。
不再发送新的数据包
当所有 on the fly 的包都得到确认之后,启动关于重传的计时器,进入 slow start 的过程
如果在重传的过程中,还发生了重传,也就是计时器超时。
重新分组。发送方不一定重传和原来一模一样的包,也可以在重传的包内包含新的数据,以增加吞吐量. 尤其是有很多小包的时候。
应该发送出去多少个重传的包呢?
如果在 slow start 的时候超时,应该传送一个 cwnd 那么多的包,cwnd = MSS.
如果在 congestion avoid 的时候超时,应该传送一个 cwnd 那么多的包,cwnd = MSS.
如果因为三个 dup ack ,那么应该发送 1 个包。
如果在 fast recover 的过程中, 每收到一个 DUP ACK ,增加 cwnd 一个 MSS ,发送一个新包。
RTO 和 RTT 的测量
RTT_Err = RTT_Delay - RTT_Ave
RTT_Ave = RTT_Ave + 0.125 × RTT_Err
RTT_Dev = 0.75 × RTT_Dev + 0.25 × | RTT_Err |
RTO = RTT_Ave + 4 × RTT_Dev
根据实现不同,可能所有的包都作测量,也可能选择某些包作测量。
由于不是每一个包都有 ack ,那么怎么选择呢?
Karn 算法:
重传过程中,不作测量。因为不知道 ACK 是对首次的包的 ACK ,还是对重传的包的 ACK
重传之后, RTO 加倍。防止连续重传。
吞吐量
吞吐量 = 带宽 * 往返时间
乱序的包
如果收到乱序的包,那么就应该立即发送 ACK ,以确保对方知道 DUPACK ,然后及时重传。
参考文献
http://www.faqs.org/rfcs/rfc2001.html RFC 2001 - TCP Slow Start, Congestion Avoidance, Fast Retransmit, and Fast Recovery Algorithms
http://www.ietf.org/rfc/rfc2581.txt TCP Congestion Control
http://www.ietf.org/rfc/rfc1122.txt RFC 1122 Requirements for Internet H osts - Communication Layers