TCP 是面向连接的、可靠的流协议。流就相当于不间断的数据结构。
TCP 之所以能够提供可靠传输就在于 通过 校验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制去实现的。
下面会通过介绍 TCP 的首部格式进行展开,一一阐述 TCP 三次握手、四次挥手、滑动窗口、拥塞控制、流量控制和 UDP 协议。
TCP首部格式
-
源端口号:表示发送端的端口号,字段长 16 位。
-
目标端口号:表示接收端端口号,字段长度 16 位。
-
序列号:字段长 32 位。序列号是指发送数据的位置。每发送一次数据,就累加一次该数据字节数的大小。
序列号不会从0开始,是在建立连接的时候由计算机随机生成一个数作为初始值,通过 SYN 包传给接收端主机。
-
确认应答号:长度是 32 位。是指下一次应该收到的数据的序列号。发送端收到这个确认应答以后可以认定这个序号之前的数据都已经被正常接收。
-
数据偏移:表示数据开始的地方离 TCP 起始处有多远。实际上也就是表示 TCP 首部的长度。
-
保留: 该字段是为了以后扩展时使用,长度是 4 位。
-
控制位:长度为 8 位。每一位从左到右分别是 CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。具体我会在讲解 TCP 三次握手和四次挥手再重点说明。
-
窗口大小:该字段用于通知相同 TCP 首部的确认应答所指位置开始能接受的数据大小。TCP不允许发送超过该大小的数据。
-
校验和:由发送端填充,接收端对TCP报文段执行CRC算法以检验TCP报文段在传输过程中是否损坏。注意,这个校验不仅包括TCP头部,也包括数据部分。这也是TCP可靠传输的一个重要保障。
-
紧急指针:该字段表示本报文中紧急数据的指针。从数据部分的首位到紧急指针所指位置为止为紧急数据。
-
选项:用于提高 TCP 的传输性能,是可变长的可选信息。最多包含 40 字节。
TCP 三次握手
三次握手说明:
-
由客户端发送建立 TCP 连接的请求报文,报文中包含 seq 序列号,是由发送端随机生成的,并且将报文中的 SYN 字段 设置为 1,表示需要建立 TCP 连接,客户端进入 SYN_SEND 状态。(SYN=1.seq=x,x代表随机生成的数值)
-
由服务器端回复客户端发送的 TCP 连接请求报文,其中包含 seq 序列号,是由回复端随机生成的,并且将 SYN 设置为 1,也会产生一个 ACK 字段,字段值是在客户端发送过来的序列号 seq 基础上加 1,以便客户端收到信息时,知道自己的 TCP 建立请求已经得到验证,服务端进行 SYN_SEND 状态。(SYN =1,ACK = x+1,seq=y,y为随机生成数值)
-
客户端收到服务端发送的 TCP 建立验证请求后,会使自己的序列号加 1 表示,并且再次回复 ACK 验证请求,在服务端发过来的 seq 上加 1 进行回复,客户端进入 ESTABLISHED 状态,当服务端接收到请求,也进入 ESTABLISHED 状态,TCP 握手结束。(SYN = 1,ACK = y+1,seq = x+1)
为什么三次握手?
1. 检查双方是否都具备发送和接收数据的能力
TCP 是基于全双工的可信传输协议,也就意味着数据可以同时在两个方向上传输。在建立三次握手的过程中也就是在检验双方发送和接收数据的能力是否具备。
第一次握手,这时候客户端知道自己具备了发送数据的能力,但还不知道服务端是否有接收和发送数据的能力。
第二次握手,当服务端接收到报文后,回复确认报文,此时服务端知道客户端具有发送报文的能力,并且知道自己具有接收和发送数据的能力,但还不知道客户端是否具有接收数据的能力。
第三次握手,当客户端收到服务端的确认报文后,知道服务端具备接收和发送数据的能力,因为服务端还不知道客户端具备接收数据的能力,所以还需要发送一个确认报文,告知服务端自己是具有接收能力的。
2.防止重复连接
在网络状况比较复杂或比较差的情况下,发送方可能会连续发送多次建立连接的请求。如果 TCP 握手的次数只有两次,那么接收方只能选择接受请求或者拒绝连接请求,但并不清楚这次的请求是否是正常的请求。
如果是三次握手的话,客户端在接收到服务端 seq+1 的消息之后,通过对比,就可以判断当前连接是否是历史连接,如果是的话就会发送终止报文给服务端终止连接。如果不是历史连接就发送确认报文建立连接。
四次挥手
建立一个 TCP 连接需要三次握手,而终止一个 TCP 连接需要经过四次挥手,这是由于 TCP 的半关闭特性造成的,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
第一次挥手:客户端发送一个 FIN 报文(请求连接终止:FIN = 1),报文中会指定一个序列号 seq = u。并停止再发送数据,但依然能够接收数据,主动关闭 TCP 连接。此时客户端处于 FIN_WAIT-1 状态,等待服务端确认。
第二次挥手:服务端收到 FIN 之后,会发送一个 ACK 报文,且把客户端的序号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。客户端收到服务端的确认后,进入 FIN-WAIT-2 状态,等待服务端发出的连接释放报文段。
前两次挥手 既让服务端知道了客户端想释放连接,也让客户端知道了服务端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务端方向上的连接的。
第三次挥手:如果服务端也想断开连接,会发送 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 状态,等待客户端的确认,并停止向客户端发送数据,但服务端仍能够接收从客户端传输过来的数据。
第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1),此时客户端处于 TIME_WAIT 状态,并在这个状态等待 2MSL。服务端收到从客户端发出的 TCP 报文之后结束 LAST-ACK 阶段,进入 CLOSED 阶段。
客户端等待完 2MSL 之后,结束 TIME-WAIT 阶段,进入 CLOSED 阶段,由此完成四次挥手。
为什么客户端在 TIME-WAIT 阶段要等 2MSL?
为的是确认服务端是否收到客户端发出的 ACK 确认报文。
当客户端发出最后的 ACK 确认报文后,并不能确定服务端能够接收到,所以在发完之后,等待 2MSL,服务端在 1MSL 内没有收到客户端发出的 ACK 确认报文,就会再次向客户端发出 FIN 报文。
窗口滑动
TCP 以 1 个段为单位,每发一个段就要进行一次确认应答的处理。这种传输方式有个缺点,包往返时间越长通信性能越低。
为了解决这个问题,引入了窗口这个概念。确认应答不再是以一个分段,而是以更大的单位进行确认,发送端发了一个段后不需要一直等待确认应答,而是能够继续发送。
窗口大小就是指无需等待确认应答而可以继续发送数据的最大值,窗口大小分为四个段。
如上图所示,白色部分就是窗口,窗口内的数据即便没有收到确认应答也可以发送出去。
滑动窗口以外的部分包括尚未发送的数据和已经确认对端收到的数据。收到确认应答后,会将窗口滑动到确认应答中的
序列号的位置。这样就可以顺序的将多个段同时发送提高通信性能。这种机制就是窗口滑动机制。
窗口滑动机制下重发处理
在使用窗口控制时,可能会遇到段丢失。在没有使用窗口控制,没有收到确认应答的数据会被重发,使用了窗口滑动,默写应答即使丢失也不需要进行重发。
结合下图所示,当某段报文丢失后,发送端会一直收到序号 1001 的确认应答,这个确认应答就像在提醒发送端"我想接收的是从 1001 开始的数据"。
出现报文丢失的情况下,同一个序号的确认应答会重复发送。当发送端连续三次收到同一个应答,就会将所对应的数据进行重发,这种重发机制叫做高速重发机制。
流量控制
TCP 提供了一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。
接收端主机向发送端主机通知自己可以接收的数据大小,发送端就会发送不超过这个限度的数据。其实这个大小就是窗口大小,窗口大小的值是由接收端决定的。
拥塞控制
因为计算机是个共享的环境,也有可能会因为其他主机之间的通信造成网络拥堵。在网络出现拥堵时,突然发送一个较大量的数据可能会导致网络瘫痪,如果在通信一开始就发送大量数据,也会引发一些问题。
TCP 为了防止这个问题,会利用一个慢启动的算法,对发送数据量进行控制。
为了调节发送的量,定义了一个叫做拥塞窗口的概念。
在慢启动的时候,将拥塞窗口大小设置为 1 个数据段(1 MSS)大小。之后每收到一个确认应答,就将窗口值加 1。
在发送数据包的时候,将拥塞窗口大小与接收端主机通知的窗口大小做对比,按它们中较小的值,发送比其还要小的数据量。
TCP 与 UDP 区分
UDP是面向无连接的协议, 是一个不具有可靠性的数据报协议。不确保消息一定会到达。
UDP 主要用于对高速传输和实时性有较高要求的通信或广播通信。例如打电话,如果使用 TCP ,数据在传输过程如果有丢失就会被重发,但这样就无法流畅的传输人的声音,会造成声音大幅度延迟。
UDP 适用范围:
- 包总量较少的通信
- 视频、音频等多媒体通信
- 限定于 LAN 等特定网络中的应用通信
- 广播通信