Skip to main content

网络互联

想一想如果你要下载文件,是不是希望文件下的越稳越好,因为你不想一次又一次地点击重新下载按钮?但如果你要直播,带来的数据量是巨大的,且不谈你需要在短短的一段时间内传输完成。这个时候,我们就有了不同传输协议的需要。

TCP适合应对前者,UDP适合后者。

TCP是面向连接的一种可靠的传输控制协议,它拥有流控以及窗口机制,用独特的三次握手四次挥手的机制为它赢来了稳定性。但是缺点也很明显,它不能太

UDP是一种不可靠无连接的用户数据报协议。诚然它只能尽力传,不稳定,会丢包,但是传输量大,可靠性要求度不高又大的音视频应用场合就可以用它。

TCP和UCP都应用在传输层,也就是OSI第4层。同样也是在TCP/IP模型的传输层。

TCP和UDP报文格式

TCP报文格式

TCP 的序号字段为 32 位,可表示 0023212^{32}-1 的序号,每个序号对应一个字节,因此序号空间大小为 2322^{32} 字节,即 4GB。但序号是循环使用的,所以实际可以传输超过 4GB 的数据,只是需要处理序号回绕问题。

这个窗口是用来做流量控制的。

偏移值和IP报头(IHL)本质一样,描述的是报头的长度,它的取值理论上是可以0~15,但是实际上只取5~15,所以这就是为什么TCP的报头是可扩展的。

报头最小20字节,最长60字节。为什么?

info

TCP报文头允许携带选项(Options),例如最大报文段长度(MSS)、窗口缩放因子、时间戳等。这些选项的长度是可变的,但必须是4字节的整数倍(如果需要,会在末尾填充0以确保对齐)。因此,添加选项后,报文头的总长度可能变成20字节、24字节、28字节……最多60字节。

由于数据偏移字段以4字节为单位,所以它能表示的长度必须是4的倍数:5×4=20,6×4=24,……,15×4=60。这就是为什么最大是60字节(15 × 4)。

理论上,4位二进制可以表示0~15,但在TCP中,报文头长度不可能小于20字节。如果数据偏移字段的值小于5(即小于20字节),那么报文头就容纳不下固定部分的20字节,这显然是不合理的。因此,任何合法的TCP报文头,数据偏移字段的值至少为5。

另外,如果报文头长度超过60字节(即超过15 × 4),由于字段只有4位,无法表示更大的长度,所以TCP规定选项部分的总长度最多为40字节(因为20字节固定部分 + 40字节选项 = 60字节)。因此,数据偏移字段的取值范围被限制在5到15之间。

为什么报头不可能小于20字节?

TCP报文头至少包含以下固定字段(共20字节,每段都是4字节对应4*8个bit位):

  1. 源端口(16位)+ 目的端口(16位)

  2. 序列号(32位)

  3. 确认号(32位)

  4. 首部长度(4位)+ 保留(6位)+ 标志位(6位)+ 窗口(16位)

  5. 校验和(16位)+ 紧急指针(16位)

这些字段加起来正好是20字节。在数据偏移字段中,20字节换算成32位字就是:20÷4=520 ÷ 4 = 5。因此,一个没有携带任何选项的TCP报文头,数据偏移字段的值就是5(二进制0101),表示报文头有5个32位字(20字节)。

如果URG位为1代表有紧急数据需要发送,一般是带外网管的数据,优先级高。

知道URG=1表示有紧急数据,那么紧急指针就负责指向这个数据在什么地方。

RST=1代表重置,遇到问题后发一个重置的报文。

PUSH:

如果想要加速上传,不想缓存,那么此时把PUSH位置1,即可马上递交应用进程。

标志位作用
URG紧急指针标志,表示本报文段中包含紧急数据,紧急指针字段有效。
ACK确认标志,表示确认号字段有效。除最初建立连接的SYN报文外,该位通常置1。
PSH推送标志,指示接收方应尽快将数据交付应用层,而不等待缓冲区填满。
RST复位标志,用于异常终止连接或拒绝非法的连接请求。
SYN同步标志,在连接建立时用于同步序列号。SYN=1表示这是一个连接请求或接受报文。
FIN终止标志,表示发送方数据已发送完毕,要求释放连接。

UDP报文格式

相比之下,UDP报文格式显得简单很多。

源端口+目的端口+长度+校验码+数据。头部只有8字节。

TCP三次握手四次挥手

TCP本质是一门状态机。

三次握手:

第一次握手(SYN):客户端主动发起连接,发送一个SYN包(同步序列编号),并带上客户端的初始序列号(client_isn),然后客户端进入SYN-SENT(同步已发送)状态。这一步主要是客户端证明自己能发报。

第二次握手(SYN + ACK):服务端收到后,如果同意连接,会回复一个SYN-ACK包。这个包包含了对客户端SYN的确认(ack = client_isn + 1),同时也带上服务端自己的初始序列号(server_isn)。此时服务端进入SYN-RCVD(同步已接收)状态。这一步证明了服务端能收报、能发报。

第三次握手(ACK):客户端收到后,需要再回复一个ACK确认包,确认收到了服务端的SYN(ack = server_isn + 1)。这个包可以携带数据。此时客户端和服务器都进入ESTABLISHED(连接已建立)状态。这一步是为了防止一种叫‘已失效的连接请求报文段’突然又传到服务端,导致服务端建立空连接而浪费资源。”

四次挥手:

第一次挥手(FIN):主动关闭方(比如客户端)发送一个FIN包,表示它这边的数据发完了,请求关闭连接。客户端进入FIN-WAIT-1(终止等待1)状态。

第二次挥手(ACK):被动关闭方(服务端)收到FIN后,回复一个ACK。此时服务端进入CLOSE-WAIT(关闭等待)状态。客户端收到ACK后进入FIN-WAIT-2(终止等待2)状态。 (这里有个关键点:服务端此时可能还有数据没传完,所以不能马上关闭,ACK只是告诉客户端‘我知道你要关了,但我这边还得传一会儿’。这就是为什么是四次挥手,而不是三次。)

第三次挥手(FIN):当服务端的数据也传输完毕后,它发送一个FIN包给客户端,表示服务端这边也想关了。服务端进入LAST-ACK(最后确认)状态。

第四次挥手(ACK):客户端收到FIN后,回复最后一个ACK,然后进入TIME-WAIT(时间等待)状态。等待2MSL(最长报文段寿命)时间后,才最终进入CLOSED(关闭)状态。”