[计算机网络读书笔记]TCP的三次握手与四次挥手
一 TCP的连接
- 每一条TCP连接有两个端点,TCP连接的端点叫做套接字(socket)或插口。
- 端口号拼接到IP地址即构成了套接字。
- 每一条TCP连接唯一地被通信两端的两个端点(即两个套接字)所确定。
二 TCP的连接建立-三次握手
- 客户端和服务器开始都处于closed状态。客户端主动打开连接,而服务器被动打开连接。
- 服务器端先创建传输控制快TCB,准备接受客户进程的连接请求。然后服务器就处于listened状态,等待客户的连接请求。
- 客户端首先创建传输控制模块TCB,然后向服务器发出连接请求报文段,首部中的同步位SYN=1,同时选择一个初始序号seq=x。TCP规定,SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这是客户端进入到SYN-SENT(同步已发送)状态。
- 服务器收到连接请求报文段,如同意建立连接,则向客户端发送确认。在确认报文段中应把SYN位和ACK位都置1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。这个报文段也不能携带数据,但同样要消耗掉一个序号。这时服务器进入SYN-RCVD(同步收到)状态。
- 客户端收到服务器的确认后,还要再次向服务器发送确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。TCP标准规定,ACK报文段可以携带数据。但如果不懈怠数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+1。这时,TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。
- 当服务器收到客户端的确认后,也进入ESTABLISHED状态。

三 为什么客户端还要发送一次确认?
- 这主要是为了防止已失效的连接请求报文段突然又传送到了服务器,因而产生错误。
- 考虑这样一种异常场景。客户端发出一个连接请求报文段,在某些网络节点长时间滞留,以致延误到客户端重传请求,建立连接,传输数据,释放连接之后才到达服务器。本来这是一个早已失效的报文段。但服务器收到此失效的连接请求报文段后,就误认为是客户端又发出一次新的连接请求。于是就向客户端发出确认报文段,同意建立连接。如果不采用三次握手,那么只要服务器发出确认,新的连接就建立了。而服务器会一直等待客户端的请求,这样服务器的许多资源就这样白白浪费了。
- 采用三次握手的办法就可以防止上述现象。在上述情况下,客户端不会像服务器的确认发出确认。服务器由于收不到确认,就知道客户端并没有要求建立连接。
四 TCP的连接释放-四次挥手
- 数据传输结束后,通信的双方都可释放连接。最初客户端和服务器都处于ESTABLISHED状态。
- 客户端先向服务器发出连接释放报文段,并停止发送数据,主动关闭TCP连接。客户端把连接释放报文段首部的FIN置1,其序号seq=u,他等于前面已传送过的数据的最后一个字节的序号加1。这时客户端加进入FIN-WAIT-1(终止等待1)状态,等待服务器的确认。TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
- 服务器收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是v,等于服务器前面已传送过的数据的最后一个字节的序号加1。然后服务器就进入CLOSE-WAIT(关闭等待)状态。服务器进程这时应通知高层应用进程,因而从客户端到服务器这个方向的连接就释放了,这时TCP连接处于半关闭状态,即客户端已经没有数据要发送了,但服务器若发送数据,客户端仍要接收。也就是,从服务器端到客户端这个方向的连接并未关闭。这个状态可能会持续一些时间。
- 客户端收到来自服务器的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待服务器发出的连接释放报文段。
- 若服务器已经没有要向客户端发送的数据,其应用进程就通知TCP释放连接。这时服务器发出的连接释放报文段必须是FIN=1。现假定服务器的序号为w(在半关闭状态服务器可能有发送了一些数据)。服务器还必须重复上次已发送过的确认号ack=u+1.这时服务器就进入LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端在收到服务器的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器(TIME_WAIT timer)设置的时间2MSL后,客户端才进入到CLOSED状态。时间MSL叫做最长报文段寿命,RFC建议设为2分钟。但这完全是从工程上来考虑,对于现在的网络,MSL=2分钟可能太长了。因此TCP允许不同的实现可根据具体情况使用更小的MSL值。因此,客户端进入到TIME-WAIT状态后,要经过4分钟才能进入到CLOSED状态,才能开始建立下一个新的连接。当客户端撤销相应的传输控制快TCB后,就结束了这次的TCP连接。
- 服务器只要收到了客户端发出的确认,就进入CLOSED状态。同样,服务器在撤销相应的传输控制块后,节结束了这次的TCP连接。

五 为什么客户端在TIME_WAIT状态必须等待2MSL的时间呢?
- 为了保证客户端发送的最后一个ACK报文段能够到达服务器。这个ACK报文段有可能丢失,因而使处在LAST_ACK状态的服务器收不到对已发送的FIN+ACK报文段的确认。服务器会超时重传这个FIN+ACK报文段,而客户端就能在2MSL时间内收到这个重传的FIN+ACK报文段。接着客户端重传一次确认,重新启动2MSL计时器。最后客户端和服务器都正常进入到CLOSED状态。如果客户端在TIME_WAIT状态不等待一段时间,而是在发送完ACK报文段后立即释放连接,那么就无法收到服务器重传的FIN+ACK报文段,因而也不会在发送一次确认报文段。这样服务器就无法按照正常步骤进入CLOSED状态。
- 防止四中提及的已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本地连接持续的时间内所产生的的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。
- 除了时间等待计时器外,TCP还设有一个保活计时器。设想这样一种情况:客户端已主动与服务器建立了TCP连接。但后来客户端的主机突然出故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再拜拜等待下去。这就是保活计时器。服务器没收到以此客户端的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75分钟发送一次。若一连发送10个探测报文段后仍无客户端的相应,服务器就认为客户端出了故障,接着就关闭这个连接。