回答思路
得分点 第一、二次分别包含数据通讯初始序号。第三次是必须的,为了防止已经失效的连接请求报文突然又被传送给了服务器端,然后产生错误。TCP是全双工通讯,客户端和服务器端都需要释放连接和接受确认,所以必须是四次挥手。 标准回答 三次握手过程:第一次握手:客户端向服务器端发送连接请求报文段,包含自身数据通讯初始序号,进入SYN-SENT状态。第二次握手:服务器端收到连接请求报文段后,如果同意,发送应答,包含自身数据通讯初始序号,进入SYN-RECEIVED状态。第三次握手:客户端收到应答,最后向服务器端发送确认报文,进入ESTABLISHED状态,此时成功建立长连接。 四次挥手过程:首先第一次挥手:客户端认为数据发送完毕,需要向服务器端发送连接释放请求。第二次挥手:服务器收到连接释放请求,告诉应用层释放TCP连接。然后发送ACK包,进入CLOSE-WT状态,此时表明客户端到服务器端的连接已经释放,不再接受客户端的数据。因为TCP是全双工的,所以服务器仍可以发送数据。第三次挥手:当服务器端数据发送完毕,向客户端发送连接释放请求,进入LAST-ACK状态。第四次挥手:客户端收到连接释放请求,向服务器端发送确认应答报文,此时客户端进入TIME-WT状态,持续2倍的MSL(最长报文段寿命),若期间没有收到服务器端的数据报文,进入CLOSED状态。服务器端收到确认应答后,也进入CLOSED状态。 加分回答 以下是客户端向服务器端发起TCP连接的详细过程: 1. 客户端和服务器端刚开始都是处于CLOSED(关闭)状态。 2. 要注意的是客户端主动打开连接,而服务器端是被动打开连接的。 3. 服务器端的进程先创建TCB(传输控制块)准备接受客户端的连接请求。 4. 客户端的进程也是先创建TCB(传输控制块),然后向服务器端发出连接请求报文段,这个报文段中的同步位SYN置为1,同时选择一个初始序号seq=x。TCP协议规定了SYN=1的报文段不可以携带数据,但是要消耗掉一个序号。这个时候客户端进入SYN-SENT状态。 5. 服务器端收到连接请求报文之后,如果同意连接,就给客户端发送确认响应。在确认报文中应该将同步位SYN和ACK都置为1,而确认号是ACK+1。这时候服务器端也需要给自己选一个初始序号seq=y。值得注意的是这个确认报文也不能携带数据,同样要消耗掉一个序号。这时服务器端进入SYN-RECEIVED状态。 6. 客户端进程收到服务器端的确认报文,最后还要向服务器端给出确认。确认报文段的ACK置为1,确认号是y+1,而自己的序号seq=x+1。TCP标准规定,ACK报文段可以携带数据,但是如果不携带数据就不消耗序号。在这个情况下,下一个数据报文的序号仍然是seq=x+1。到这时,TCP连接已经成功建立,A进入ESTABLISHED(已建立连接)状态。 到此TCP连接三次握手的过程就全部结束了。但是为什么一定要三次握手而不是两次,为什么客户端最后还需要发送一次确认报文呢?其实主要是为了防止已经失效的连接请求报文突然又被传送给了服务器端,然后产生错误。假设现在有一种情况,客户端发出的第一个连接请求报文段并没有丢失而是在某些网络节点上被滞留了,直到客户端和服务器端的新连接已经释放后的某个时间点,第一个连接请求报文段才到了服务器端,这时候服务器端以为客户端又发起了一次请求,于是服务器端向客户端发起了确认连接报文段,同意连接。假设不采用三次握手,这时候连接已经建立了,但是客户端并不知道这个情况,服务器端会一直等待客户端的数据报文,这样服务器端的资源就会被浪费,占用大量的资源。所以采用三次握手可以防止这种现象,保护网络和系统资源。 TCP连接释放的过程比较复杂,客户端和服务器端都可以主动释放连接。下面是从客户端主动释放连接为例讲解四次挥手的详细过程: 1. 客户端的应用进程先向TCP发出一个连接释放报文段,然后停止发送数据报,主动关闭TCP连接。客户端需要将连接释放报文段首部的终止控制FIN置为1,序号设置为u,u相当于前面传输的数据报文段的最后一个字节的序号加1。这时候客户端进入FIN-WT-1(终止等待1)状态,等待服务器端的确认。需要注意的是,FIN报文段也是即使不携带数据,它也消耗一个序号。 2. 服务器在收到客户端发来的连接释放报文段请求之后就发出确认,确认号ack=u+1,这个报文段自己的序号是v,v相当于之前已经传送出去的最后一个报文段的序号加1。这时候服务器端进入CLOSE-WT(关闭等待)状态,这时候服务器端的TCP进程就要通知应用进程,客户端到服务器端的连接已经关闭了。需要注意的是,这个时候的TCP连接就处于一个半关闭(half-colse)的状态,尽管客户端已经没有数据要发送了,但是服务器端还是可以向客户端发送数据的,服务器端到客户端的连接并没有被释放掉。 3. 如果服务器端也没有数据要发送给客户端了,那么应用进程就通知TCP释放连接。这时候服务器端发出的连接释放报文段请求的终止指令FIN也置为1。这时候服务器端的序号已经是w了,因为在半关闭状态服务器端可能又发送了一些数据,服务器也必须重复上次已经发送过的确认号ack=u+1。这时候服务器端进入LAST-ACK(最后确认)状态,等待客户端的确认。 4. 客户端收到服务器端的连接释放请求报文段之后,必须发出确认。在确认报文段中把ACK置为1,确认号ack=w+1,而自己的序号是seq=u+1(根据TCP标准,FIN消耗了一个序号),然后进入TIME-WT(时间等待)状态,这时候连接并没有释放掉,必须等到2倍的MSL(最长报文段寿命)之后,连接才会释放。