前言
数据从一台主机发送到网络中的另一台主机的过程
- 双方需要有相互沟通的意向(不能某一端强制传输吗?)
- 有能够沟通的物理渠道(物理链路):电话?面对面交流?
- 语言要能沟通,明白什么说话该自己说话,什么时候该对方说话**(通信协议)**
TCP协议
如何建立和关闭TCP连接?
TCP报文
简单介绍一些TCP报文的格式 TCP报文分为 TCP首部 和 数据 两部分 TCP报文首部 最小有20个字节
- 源端口和目的端口:各占2字节,写入源端口和目标端口
- 序号seq:4个字节。TCP传输的数据编号
- 确认号:4个字节,期望收到对方下一个报文段的一个数据字节的序号。如果确认号是N 代表序号N-1为止的所有数据都已经正确收到
- 数据偏移:占4字节,实际是TCP报文段的首部长度,指出TCP报文数据起始处到TCP报文段的起始处的距离。由于首部中有长度不确定的选项字段,因此数据偏移字段是必要。
- 标志字段:占6字节,URG是紧急标志,URG=1时告诉系统此报文段中有紧急数据。ACK是确认标志,ACK=1时表示成功接收了报文段。SYN是同步标志,在建立连接时用来同步序号,当前SYN=1 ACK=0表示一个连接请求报文段,响应时SYN=1,ACK=1。因此SYN=1表示一个连接请求报文。FIN是终止标志,用来释放一个连接,FIN=1表示报文发送方的数据已经发送完毕,并要求释放连接。
- 接收窗口:2个字节,指的是发送本报文段一方的接收窗口,告诉对方允许发送的数据量。用来限制发送方的发送窗口,因为接收方的数据缓存空间是有限的。
- 检验和:占2字节,检验范围包括首部和数据两个部分
建立TCP连接
过程就是经典的三次握手。 TCP是全双工通信,双方(客户端和服务端)都可以发起建立连接的请求!假设A是客户端,B是服务器
三次握手
假设A向B发起数据时
-
初始时,A和B都处于CLOSED状态,B会创建传输进程控制块TCB并进入LISTEND状态【socket bind listen】,监听端口是否接收到了TCP请求以便及时响应。 -
第一次握手:当A要发生数据时就向B发送一个连接请求报文,TCP规定连接请求报文的SYN=1,ACK=0(确认标志),SYN不可以携带数据,但是需要消耗一个序号,假设此时A发送的序号seq为x,发送完之后就进入了SYN-SENT同步已发送状态 -
第二次握手:当B收到A的连接请求报文后,如果同意建立连接就会发送给A一个确认连接请求报文,其中SYN=1,ACK=1,ack=x+1(确认号),seq=y(序号),ack的值为A发送的序号加1,ACK可以携带数据,如果不携带的话则不消耗序号,发送完之后,B进入SYN-RCVD同步已接收状态。 -
第三次握手:当A收到B的确认连接请求报文后,还要对该确认再进行一次确认,报文的ACK=1,ack=y+1,seq=x+1,发送后A进入ESTABLISHED状态,当B接收到该报文后也进入ESTABLISHED状态,客户端会稍早于服务器端建立连接。 在socket编程中,客户端执行connect()时,将触发三次握手。
为什么要三次握手
-
信息对等 为了实现可靠的数据传输,TCP协议的通信双方,都必须维护一个序列号,以标识发出去的数据包中,哪些是已经被对方收到的。三次握手的过程即通信双方相互告知序列号的起始值,并确认对方已经收到了序列号起始值的必经步骤。如果只有两次握手,只有连接发起方的起始序号能够被确认。 简单的说:双方要分别确认自己和对方的发送接收能力正常。两次握手,从B的角度来说,不能确定自己的发送能力和对方的接收能力。 -
防止超时:防止失效连接突然到达导致脏连接。网络报文的生存时间往往是大于TCP请求超时时间。A的超时连接请求可能会在双方释放连接之后到达B,B误以为A创建了新的连接请求。B发送确认报文创建连接,因为A不在SYN-SENT状态,所以建立失败,直接丢弃B的确认数据。如果是两次握手连接就建立了。 【两次握手不能用SYN-SENT状态来判断吗?】
四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次挥手。这是因为TCP半关闭(half-closed)造成的。半关闭:TCP提供连接一端在结束发送后还可以接收来自另一端数据的能力。(即断开后不能发送但是可以接收数据)
客户端和服务器均可以主动发起挥手动作。 刚开始双方都处于ESTABLISHED状态,加入是客户端先发起关闭请求,四次挥手过程如下:
-
第一次挥手:客户端发送一个终止连接报文(FIN报文),报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态。 即:发送连接释放报文段(FIN=1,序号seq=u)并停止发送数据,主动关闭TCP连接,进入FIN-WAIT1(终止等待1)状态,等待服务端的确认。 u为之前客户端发送的最后一个序号+1. -
第二次挥手:服务端收到FIN之后,会发送ACK(确认)报文。 即服务端收到连接释放报文段后,发出确认报文段(ACK=1 确认号ack=u+1 序号seq=v)服务端进入CLOSE-WAIT状态,此时TCP处于半关闭状态,客户端到服务器的连接释放。客户端收到服务端确认后,进入FIN-WAIT2状态,等待服务端发出的连接释放报文段。(*v为之前服务端发送的最后一个序号+1.) -
第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发送FIN报文(终止连接报文) 即:服务端没有要向客户端发出的数据,服务端发出终止连接报文段(FIN=1 ACK=1 序号seq = w, 确认号ack = u+1)服务器端进入LAST-ACK(最后确认)状态,等待客户端的确认。 -
第四次挥手:客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1 seq=u+1 ack=w+1) 客户端进入 TIME-WAIT(时间等待)状态,此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端进入CLOSED状态。服务端收到确认后进入CLOSED状态,稍早于客户端。
在socket编程中,任何一方执行close()操作即可产生挥手操作。
挥手为什么需要四次
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
2MSL等待状态
TIME-WAIT状态也被成为2MSL等待状态。 每个具体的TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime),他是任何报文段被丢弃前在网络内的最长时间。这个时间是有限的,因为TCP报文段以IP数据报在网络内传递,而IP数据报则有限制其生存时间的TTL字段。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME-WAIT状态停留2倍MSL的时间。这样可以让TCP再次发送最后的ACK以防止这个ACK丢失。
这种2MSL等待的另一个结果是这个TCP连接在2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
四次挥手释放连接时,等待2MSL的意义?
参考 https://zhuanlan.zhihu.com/p/86426969
|