概念
TCP 是通过三次握手建立双向连接的可靠传输协议,其本质是交换携带控制信息的 TCP 段。
过程
建立TCP连接:
第一次握手, 客户端向服务器发送一个SYN段(SYN=1, seq=ISN_C),请求同步。(同步意味着连接,这条消息是询问:你好,服务器,你能为我打开一个连接吗)
第二次握手,服务器回复一个SYN-ACK段(SYN=1, ACK=1, seq=ISN_S, ack=ISN_C+1)。表示同步和确认。(服务器确认以及同意了客户端的连接请求,并要求客户端打开连接)
第三次握手,客户端回复一个ACK段(ACK=1, seq=ISN_C+1, ack=ISN_S+1)(表示客户端也收到了服务器发来的连接请求)
双方均收到对端的 SYN 并回复 ACK 后,进入 ESTABLISHED 状态,即他们之间就建立了连接......
前文使用的 SYN段、SYN-ACK段、ACK段 都是 TCP段 的子类。
为保持术语层级清晰,后文将统一使用 TCP段 作为基础术语。
SYN
可以把SYN和ACK看作信号灯,设置初始序列号的时候,SYN的值就会为1,否则为0
ACK
ACK=1用来表示收到有效数据,“我已收到该序号前的所有字节”
seq是干什么的呢?是用来给TCP段确认顺序
第一次握手时,发送方的第一个TCP段会随机生成一个初始序列号即ISN,而seq的值将会在随机生成的ISN的值的基础上加一,例如客户端发的第一个TCP段的seq是1000,那么客户端发的第二个的TCP段的seq将是1001
所以,图内 第一次握手,客户端发送自己的第一个TCP段,seq = 1000 (ISN_C)
第二次握手,服务器发送自己的第一个TCP段,seq = 5000 (ISN_S), ack = 1001 (ISN_C + 1) 第三次握手,客户端发送自己的第二个TCP段,seq = 1001 (ISN_C + 1), ack = 5001 (ISN_S + 1)
ack是干什么的呢?是用来告诉对方下一个应该接受的TCP段
再来回顾图中内容,第一次握手客户端发送给服务器的是序列号为1000的TCP段,那么服务器下一次发送的应该是序列号为1001的TCP段,
所以第二次握手时,服务器在ack的值里提醒了客户端,即ack=1001,它告诉客户端你这次发的是1000,下次我收到的应该是序列号为1001的TCP段
服务器发给客户端也是同理
seq和ack都保证了双方不会漏收数据
好处
-
三次握手可以验证当时握手的双方都有接受和发送的能力
-
SYN报文里的初始序列号(seq)可以用来区分新旧报文,避免网络延迟导致历史连接混乱(比如重复发送的上一次已经关闭的TCP连接发送的SYN报文,被误认为新的SYN报文)
-
握手双方交换
ISN_C和ISN_S,保证后续数据包的有序传输 -
防止资源耗尽,结合
SYN Cookie机制抵御SYN洪水攻击
扩展
SYN Cookie工作流程:
- 收到SYN → 计算哈希值
H = F(源IP+端口+目标IP+端口+密钥) - 发送SYN-ACK,其中包含的
ISN_S = (H << 24) + 时间戳 - 收到ACK → 验证确认号
ack_num - 1 == H? 分配资源 : 丢弃
SYN洪水攻击:
前提:服务器收到SYN报文(第一次握手请求),回复SYN-ACK后会进入半开连接(SYN_RCVD) 状态,等待第三次握手,而半开连接会占用内核资源(如连接队列、内存等)
原理:攻击者伪造大量虚假IP地址,向目标服务器发送海量SYN报文,服务器收到SYN报文并回复SYN-ACK后,服务器进入半开连接(SYN_RCVD) 状态,而虚假IP不会回复第三次握手的ACK报文,服务器会持续等待直到超时
影响:导致新的合法SYN请求被拒绝;系统资源被无效连接占满;正常用户无法建立TCP连接