参考:计算机网络微课堂——湖科大
这三个阶段就是我们听说最久的:
三次挥手建立连接、建立连接后进行数据传输、四次挥手释放TCP连接
TCP的连接建立要解决以下三个问题:
具体过程:
在三报文握手过程中,有两个角色,客户端发送握手连接请求,服务器等待接收请求。
在最开始:双方都是处于关闭状态,此时双方的TCP进程都是关闭的。
准备过程中,服务器创建传输控制块,在块中有TCP连接表等一些TCP连接的重要信息。创建后便开始监听,准备接收来自客户端的连接请求。
值得注意的是:
TCP服务器进程是被动等待来自TCP客户进程的连接请求。而不是主动发起,因此称为被动打开连接。
与服务器类似,客户端进程也需要进行准备:
其准备过程只有建立传输数据块。
准备完毕后开始”握手“:
第一次发送握手报文:
在打算建立TCP连接时,客户端向TCP服务器进程发送TCP连接请求报文段,并进入同步已发送状态
TCP连接请求报文段首部中,有两个关键数据:
- 同步位SYN:被设置为1,表明这是一个TCP连接请求报文段
- 序号字段seq:被设置了一个初始值x,作为TCP客户端进程所选择的初始序号
请注意:TCP规定SYN被设置为1的报文段不能携带数据,但要消耗掉一个序号
服务器收到信号后,发送第二次握手报文:
TCP服务器进程收到客户端发送的TCP连接请求报文段后,如果同意建立连接,则向TCP客户进程发送TCP连接请求确认报文段,并进入同步已接收状态
TCP连接请求确认报文段首部中有几个关键数据位:
同步位SYN和确认为ACK:都设置为1,表明这是一个TCP连接请求确认报文段。
序号字段seq:设置了一个初始值y,作为TCP服务器进程所选择的初始序号。
确认号字段ack:值被设置成了x+1,这是对TCP客户进程所选择的初始序号(seq)的确认。
值得注意的是:这个报文段也不能携带数据,因为它是SYN被设置为1的报文段,但同样要消耗掉一个序号
发送方接收到确认报文后,开始第三次握手:
TCP客户进程收到TCP连接请求确认报文段后,还要向TCP服务器进程发送一个普通的TCP确认报文段,并进入连接已连接状态
普通的TCP确认报文段首部中有以下重点数据位:
确认位ACK:设置为1,由于没有SYN同步字段,表明这是一个普通的TCP确认报文段,表示已确认收到建立连接报文。
序号字段seq:设置为x+1,因为TCP客户进程发送的第一个TCP报文段的序号为x,所以TCP客户进程发送的第二个报文段的序号为x+1(SYN字段报文需要消耗一个序号)
确认号字段ack:设置为y+1,这是对TCP服务器进程所选择的初始序号的确认
值得注意的是:TCP规定普通的TCP确认报文段(只有ACK没有SYN)可以携带数据,但如果不携带数据,则不消耗序号
三次握手完毕后,服务器也进入连接已建立状态,此时双方连接已建立,可以开始进行数据传输。
这里有一个问题:
为什么TCP客户端进行最后还要发送一个普通的TCP确认报文段呢?,是否多余?
答案是否定的。
例如,下面采用两次握手就建立连接:
客户端发出去了第一个连接请求报文并没有丢失,但是由于某种原因造成了较长时间的迟到现象。
此时发送端重新开始发送请求连接报文,并且通过两次握手成功建立连接,没有进行请求序号的验证。
在传输完数据之后,通过四次挥手释放连接。此时双方处于关闭连接状态。
但是此时,已经迟到多时的请求报文到达了服务器,本来这是一个早已失效的报文段,但是服务器收到此失效的报文之后,会误认为是客户端再次发出的一个新的连接请求。
于是服务器就向客户端又发出确认报文,表示同意建立连接。此时服务器处于连接状态,而客户端由于处于关闭状态,不会接收该确认报文。
如果不采用“三次握手”,服务端接收到客户端发出的请求报文就会认为要请求建立新的连接,并且进入连接状态,但是客户端此时可能并没有发出建立连接的请求,该报文是由于某种情况迟到的请求,因此不会去向B端发送数据,服务端没有收到数据就会一直等待。
这样子会造成服务端白白浪费掉很多资源。
综上所诉,三次握手并不多余,这是为了防止已失效的连接请求报文段突然又传送到了TCP服务器,因而导致错误。
总结:
以下图解说明:
在双方连接已建立后,此时需要客户端主动发出关闭信号
TCP客户进程会发送TCP连接释放报文段,并进入终止等待1状态
TCP连接释放报文段首部中有以下关键数据位:
终止位FIN和确认位ACK:值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认
序号seq字段:值设置为u,u等于TCP客户进程之前已传送过的数据的最后一个字节的序号加1,用来表示发送过程中的最后一个字节序号为u。
确认号ack字段:值设置为v,v等于服务器进程之前发送的数据中最后一个字节的序号加1,也就是确认收到已经收到的服务器发送的数据
接收方再接收到连接释放报文后,会发送一个普通的TCP确认报文段并且进入关闭等待状态。
普通的TCP确认报文段首部中有以下关键数据位:
确认位ACK:值被设置为1,表明这是一个普通的TCP确认报文段。
序号seq:值设置为v,v等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1,与之前收到的TCP连接释放报文段中的确认号ack值匹配
确认号ack字段:值为u+1,这是对TCP连接释放报文段的确认,也就是为发送方连接释放报文中的seq的值+1.
在进行第二次挥手后,会产生以下过程:
TCP服务器进程通知高层应用进程,TCP客户进程要断开与自己的TCP连接,此时的TCP连接进入半关闭状态。
所谓半关闭状态可以认为,此时客户端与服务器的连接不再传输数据,也就是客户端没有数据在发生。而此时服务器若有剩余数据要发送会继续发送。客户端到服务器这一信道关闭了,而服务器到客户端这一半没有关闭,因此称为半关闭状态。
这个半关闭状态可能会持续一段时间,直到发送方没有数据进行发送。
在以上等待过程中,客户端会进入终止等待2状态:
此时当服务器没有数据要传输后。进行第三次挥手:
此时TCP服务器进程会发送TCP连接释放报文段并进入最后确认状态。
在该报文段中有以下关键数据段:
终止位FIN和确认位ACK:值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认
序号seq:值为w,因为在半关闭状态下,TCP服务器进程可能又发送一段数据,因此w就是该段数据最后的序号。
确认号ack:值为u+1,这是对之前收到的TCP连接释放报文段的重复确认,因此值与发送方第一次挥手发送的seq值+1。
此时针对第三次挥手的报文段发送普通的TCP确认报文段,之后进入时间等待状态。
该报文段首部中有以下关键数据位:
确认位ACK:值被设置为1,表明这是一个普通的TCP确认报文段。
序号seq字段:值设置为u+1,用来表示最后一个发送的字节序号,但是为何没有发送数据,而此时值要设置为u+1(对比第一次挥手数据)?,因为TCP客户进程之前发送的TCP连接释放报文段(带有FIN)虽然不携带数据,但要消耗掉一个序号。
确认号ack:值设置为w+1,这是对所收到的TCP连接释放报文段的确认
此时,TCP服务器进程收到该报文段后就进入关闭状态,而TCP客户进程还要经过2MSL后才能进入关闭状态:
MSL具体的值可以根据TCP协议的不同实现进行设置。
至此,三次握手四次挥手过程结束!
此时就会出现一个问题:
答案是有必要。
考虑以下情况:
若客户端发送完最后一次报文后,也就是第四次挥手后就直接进入关闭状态,此时若第四次挥手报文丢失,会导致服务器的超时重传。
此时客户端又已经关闭,导致不接受该报文,因此服务器会一直不断重传,并一直处于最后确认状态无法进入关闭状态。
因此,有以下结论:
客户端进入时间等待状态以及处于该状态2MSL时长,可以确保TCP服务器进程可以收到最后一个TCP确认报文段而进入关闭状态。
TCP客户进程在发送完最后一个TCP确认报文段后,在经过2MSL时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中,不会出现旧连接中的报文段。
若出现这样一种情况:
TCP双方已经建立了连接,但是传输过程中TCP客户进程所在的主机出现了故障,此时TCP服务器进程以后就不能再收到TCP客户进程发来的数据,这时服务器进程会一直处于等待状态。
为了使TCP服务器进程不要再白白等待下去出现了TCP保活计时器: