TCP/IP Packet

- IP Packet = IP Header + TCP header + TCP Payload(segment)
- IP payload = TCP packet = TCP header + TCP Payload(segment)
- IP Packet 不应该超过 MTU (Maximum transmission unit)
- TCP Payload(segment) 不应该超过 MSS (Maximum segment size)
TCP Sequence Numbers
ISN
在建立TCP连接后, 系统会随机选取一个起始序列号ISN (Initial Sequence Number), 那么主机发送的第一个数据字节是 ISN + 1,第二个是 ISN + 2, 第三个是 ISN + 3…即TCP Payload的第一个字节是ISN + 1, 之后再顺序递增。这增强了TCP的安全性, 使攻击者没有办法伪造一个TCP packet。
ACK && ack
假如接收方最后一个收到的字节序列是 ISN + 219 那么接收结束后, 它的ACK标志位为1,确认号字段有效。它的确认号ack是 ISN + 220。即告诉发送方它下一个期待的数据的位置是哪里。注意这两个是不一样的。
SYN
SYN=1表示发送方声明:当前 Seq Number 字段的值是我这一侧的初始序列号。接收方应当把后续 seq 理解为 ISN + 偏移。
Maintaining State (Full Duplex, Connection Setup and Teardown)
TCP is Full-Duplex (全双工)
TCP 连接建立后,双方可以同时、独立地在两个方向上发送数据,互不影响。
Three-Way Handshake (三次握手)
包 ①:客户端 → 服务器(CLOSED → SYN-SENT)
┌───────────────────────────────────┐
│ SYN=1 ← 旗子:这是初始序列号
│ seq=ISN_C ← 我从这开始计数
│ ACK=0 ← 忽略
└───────────────────────────────────┘
发送包1后,客户端:CLOSED → SYN_SENT
收到包1后,服务器:LISTEN → SYN_RCVD
包 ②:服务器 → 客户端(LISTEN → SYN-RCVD)
┌───────────────────────────────────┐
│ SYN=1 ← 旗子:这也是初始序列号
│ ACK=1 ← 确认前一个包收到了
│ seq=ISN_S ← 我这边的起点
│ ack=ISN_C+1 ← eq=ISN_C 我已收到
└───────────────────────────────────┘
收到包2后,客户端:SYN_SENT → ESTABLISHED
包 ③:客户端 → 服务器(SYN-SENT → ESTABLISHED)
┌───────────────────────────────────┐
│ ACK=1 ← 纯确认
│ seq=ISN_C+1 ← 因为①消耗了1
│ ack=ISN_S+1 ← 服务器的ISN收到了
└───────────────────────────────────┘
收到包3后, 服务器:SYN_RCVD → ESTABLISHED
注意, 都是各自端的SYN消耗了服务器端和客户端的ISN第一位,ACK不消耗占位。
TCP Connection Teardown
Four-Way Handshake (四次挥手)
首先阐述一下FIN的含义:
FIN报文表示:我的数据已经发完。不再向你发送的新的数据,但是还可以接收你发送的数据。
第一次挥手 (客户端 -> 服务端):
报文:FIN (finish). 占用序列号 seq_c_1
客户端进入 FIN_WAIT_1
第二次挥手 (服务端 -> 客户端):
报文:ACK (Acknowledgment). ack_s_1 = seq_c_1 + 1, seq_s_1 = seq_s_0 (ACK不消耗)。
服务端进入 CLOSE_WAIT 客户端进入 FIN_WAIT_2。
第三次挥手 (服务端 -> 客户端):
报文:FIN. seq_s_2 = w (如果没有新的数据要发,那么 w 就是第二次挥手的seq_s_1,因为ACK并不占用字节序号。如果有新的数据要发,(可能服务端还有一些数据没有发送完全),那么w不确定。),
ack_s_2 = ack_s_1。
服务端进入 LAST_ACK,客户端收到后进入TIME_WAIT。
这个时候服务端处理完了所有待发送数据,表示自己也可以关闭连接了。
第四次挥手 (客户端 -> 服务端):
报文:ACK. ack_c_1 = seq_s_2 + 1, seq_c_2 = seq_c_1 + 1
客户端进入TIME_WAIT 服务端收到后进入CLOSED
客户端进入TIME_WAIT后,会持续2MSL(Maximum Segment Lifetime,报文最大生存时间,一般 30 秒、1 分钟或 2 分钟),以此保证ACK被对方收到,使这次连接的“迷途”报文自然消失。
Abrupt Termination (异常终止)
TCP除了一般的四次握手关闭,还支持异常终止关闭。这种中断使用RST (Reset) 报文,而不是FIN。单方发送RST,立刻终止连接。直接进入CLOSED。不消耗序列号,接收方看到RST后立刻终止连接,不发送任何回应直接丢弃。
Piggybacking (稍待确认)
在TCP中,Piggybacking指将ACK确认信息搭载在一个发送数据段上,而不是单纯发送一个无数据的ACK。
为什么能这样操作?ACK不占用序列号是一个重要原因,使它可以非常便利地搭便车。
但是实现 Piggybacking 有难点:
因为TCP 实现在操作系统内核,而应用程序在用户态,两者分离带来问题:内核收到数据包后,未必知道应用程序是否会马上回复数据。如果内核为了等数据而延迟发送 ACK(延迟确认),而应用程序其实没有数据要发,那么对方就会因 ACK 超时而重传,增加延迟。而 TCP 必须使用延迟确认计时器(通常 40-200ms),计时器到期还没有应用数据,就单独发一个纯 ACK。
但是SYN-ACKs 没有这个难点,这两个动作都是由 TCP 内核本身发出的,不依赖应用程序是否立即响应。所以SYN-ACKs一般形成Piggybacking一起发送。
Slinding window
滑动窗口工作在两台主机的TCP缓冲区中。我们站在发送方的角度来看:
累积确认 (Cumulative Acknowledgment)
这是 TCP 确认机制的核心方式,它直接配合滑动窗口工作。ack号表示该序列号之前所有的字节也已正确收到。

绿色指的是已发送且已确认的数据包,白色指已发送未确认的数据包,灰色指未发送的数据包
图片上说的已经很清楚了。只有当收到 >= 15的ack的时候窗口才会滑动,可以继续发Not sent yet中的包.
W被两个值决定:
通告窗口 (rwnd):接收方根据自己的接收缓冲区剩余空间通告的限制,用于流量控制,防止发送方淹没接收方。
拥塞窗口 (cwnd):发送方根据网络丢包、延迟等感知到的拥塞程度动态调整的限制,用于拥塞控制,防止发送方淹没网络(路由器)。
取这两者的最小值
Detecting Loss
- Ack-based (快速重传): 当接收方收到一个失序段,会重复发送之前的ack号。当发送方收到3个或者更多重复ack时,判定该段已经丢失,重新发送丢失的包
- Timer-based (超时重传): 整个发送窗口仅仅维护一个全局计时器,始终针对发送窗口最左的未确认数据包。如果计时器到超时仍然没有收到该包的ack,超时后重传从丢失包开始的所有未确认包。
值得注意的是,课件中并不是标准TCP的实现,而是纯GBN (Go-Back-N),不应该把它当作标准TCP计时器模型或者现代TCP计时器模型看待。
TCP Header

看这张图就够了。其中Flags有8个。我们关心其中的四个:SYN,ACK,FIN,RST。具体的功能作用在上文已经有提及,在此不予赘述。其中有很多东西并没有提及,那些不是目前的重点,在此也不予赘述。
评论(0)
暂无评论