1. 三次握手
- 请求端(通常称为客户)发送一个 S Y N段指明客户打算连接的服务器的端口,以及初始序号。这个S Y N段为报文段1。
- 服务器发回包含服务器的初始序号的 S Y N报文段(报文段2)作为应答。同时,将确认序号设置为客户的I S N加1以对客户的S Y N报文段进行确认。一个S Y N将占用一个序号。
- 客户必须将确认序号设置为服务器的 I S N加1以对服务器的S Y N报文段进行确认(报文段3)。
除了交换彼此的初始序列号,三次握手的另一个重要作用是交换一些辅助信息,比如最大段大小(MSS)、窗口大小(Win)、窗口缩放因子(WS)、是否支持选择确认(SACK_PERM)等
1.1 初始序列号(Initial Sequence Number, ISN)
初始的序列号并非从 0 开始,通信双方各自生成,一般情况下两端生成的序列号不会相同。生成的算法是 ISN 随时间而变化,会递增的分配给后续的 TCP 连接的 ISN。
1.1.1 ISN 能设置成一个固定值呢?
1、出于安全性考虑。如果被知道了连接的ISN,很容易构造一个在对方窗口内的序列号,源 IP 和源端口号都很容易伪造,这样一来就可以伪造 RST 包,将连接强制关闭掉了。如果采用动态增长的 ISN,要想构造一个在对方窗口内的序列号难度就大很多了。
2、因为开启 SO_REUSEADDR 以后端口允许重用,收到一个包以后不知道新连接的还是旧连接的包因为网络的原因姗姗来迟,造成数据的混淆。如果采用动态增长的 ISN,那么可以保证两个连接的 ISN 不会相同,不会串包。
RFC 793指出I S N可看作是一个3 2比特的计数器,每4 m s加1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它作错误的解释。
1.2 三次握手的状态变化
- 对于客户端而言:
-
初始的状态是处于 CLOSED 状态。CLOSED 并不是一个真实的状态,而是一个假想的起点和终点。
-
客户端调用 connect 以后会发送 SYN 同步报文给服务端,然后进入 SYN-SENT 阶段,客户端将保持这个阶段直到它收到了服务端的确认包。
-
如果在 SYN-SENT 状态收到了服务端的确认包,它将发送确认服务端 SYN 报文的 ACK 包,同时进入 ESTABLISHED 状态,表明自己已经准备好发送数据。
- 对于服务端而言:
- 初始状态同样是 CLOSED 状态
- 在执行 bind、listen 调用以后进入 LISTEN状态,等待客户端连接。
- 当收到客户端的 SYN 同步报文以后,会回复确认同时发送自己的 SYN 同步报文,这时服务端进入 SYN-RCVD 阶段等待客户端的确认。
- 当收到客户端的确认报文以后,进入ESTABLISHED 状态。这时双方可以互相发数据了
1.3 同时打开的状态变化
两个应用程序同时彼此执行主动打开的情况是可能的,尽管发生的可能性极小。每一方必须发送一个 S Y N,且这些S Y N必须传递给对方。这需要每一方使用一个对方熟知的端口作为本地端口。这又称为同时打开( simultaneous open)。
例如,主机A中的一个应用程序使用本地端口 7 7 7 7,并与主机B的端口8 8 8 8执行主动打开。主机B中的应用程序则使用本地端口 8 8 8 8,并与主机A的端口7 7 7 7执行主动打开。
这与下面的情况不同:主机A中的Te l n e t客户程序和主机B中Te l n e t的服务器程序建立连接,与此同时,主机 B中的Te l n e t客户程序与主机A的Te l n e t服务器程序也建立连接。在这个 Te l n e t例子中,两个Te l n e t服务器都执行被动打开,而不是主动打开,并且 Te l n e t客户选择的本地端
口不是另一端Te l n e t服务器进程所熟悉的端口。
T C P是特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连
接
两端几乎在同时发送 S Y N,并进入S Y N _ S E N T状态。当每一端收到 S Y N时,状态变为S Y N _ R C V D,他们都再发S Y N并对收到的S Y N进行确认。当双方都收到 S Y N及相应的A C K时,状态都变迁为E S TA B L I S H E D。图1 8 - 1 7显示了这些状态变迁过程。
参考资料
《深入理解 TCP 协议:从原理到实战》