二、 传输层
传输层的核心作用是提供 "端到端" 的逻辑通信信道。
1. UDP协议
1.1 UDP 协议端格式

2. TCP协议
2.1 TCP协议端格式

4位首部长度: 首部指的是图上的前6行,前5行是固定的长度,20个字节,第6行是变长,TCP头部的长度表示的是固定长+变长。TCP头部最大长度是15*4byte=60byte.
6位标志位:
默认都是0,进行操作时,将对应的标志位置为1。
-
URG:紧急指针是否有效,表示此段中包含紧急数据,应被优先处理。
-
ACK:确认号是否有效,应答标志 。
-
PSH:推送功能。提示接收端应该尽快将数据交付给应用层,而不是在缓冲区中等待更多数据。
-
RST:对方要求重新建立连接。我们把携带RST标志的称为复位报文段。
-
SYN:请求建立连接,我们把携带SYN标识的称为同步报文段,可以简单理解为发送标志 。
-
FIN:通知对方,本端要关闭了,要求释放连接。我们称携带FIN标识的为结束报文段, 断开标志。
2.2 确认应答
假设现在A、B在发信息,A每发送一个信息,都会得到来自B的一个应答。但是如果A同时发送多条信息,B会应答相应的条数,就会出现应答是错位的,分不清每个应答针对的是哪一条信息。
注意:应答和响应是不一样的。
-
应答只是一个标记,并没有真实的结果,仅仅是为了告诉发送方我收到了。
-
响应是针对请求计算出来的响应。
针对这种情况,TCP将每个字节的数据都进行了标号,即为序列号 。

每一个ACK都带着对应的确认序列号,表示自己已经收到了哪些数据,下一次你从哪里开始发送。

对应到上面的32位序号和32位确认序号:

每一端都既是发送方也是接收方。
自己的发送序列号:由发送方维护,用于标记它发出的数据。
自己的接收确认号:由接收方维护,用于确认它收到的数据。
2.3 超时重传(可靠性机制)
数据在网络上传输的过程中会经过很多网络设置,比如路由器、交换机、运营商、如果其中一个设置出现了问题,这个请求会超时。
1. 发送超时

主机B没有收到数据。A在达到超时时间后再发送一次。
2. 接收方收到了数据,返回应答时超时了

此时,主机A并不能确认究竟是发送丢包还是应答丢包。A不管究竟发生了什么,只要自己没有收到应答,在到达超时时间后就会再次发送数据。
那么对于主机B来说,他接收到了两次相同的数据,此时,主机B就能根据序列号来确认这个包是不是重复的包,如果是重复的,就会直接舍弃这个包,也就达到了去重的效果
超时重传的时间如何进行确认呢?
理想情况下这个时间越小越好,能够保证确认应答一定能在这个事件内返回就行。
但是这个时间的长短,是会随着网络环境的不同而变化的。
如果超时时间设置的太长,会影响整体的重传效率;如果设置的太短,可能会频繁发送重复的包。
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。
超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后,仍然得不到应答,等待2* 500ms再次进行重传;如果仍然得不到应答,等待4*500ms进行重传,以此类推,以指数形式递增。累计到一定的重传次数,TCP认为发生异常,强制关闭连接。
2.4 连接管理(可靠性机制)
作用:在发送方和接收方初次建立连接的时候,确认双方的收发能力。

2.4.1 初次建立连接时,三次握手
主要是验证网络状态,保证连接没有问题。

-
第一次握手
主机A向主机B发送一个TCP数据包,同时会把这个TCP数据包中的SYN置为1,表示这是一个连接请求报文,并随机生成一个序列号填充在32位序号区域。

-
第二次握手(ACK + SYN)
主机B在收到主机A发送的TCP数据报之后,会给发送方返回一个应答包,同时会把标志位ACK置为1,表示这是一个应答包。并将主机A发送的序列号 + 1 填充在确认序号区域。
与此同时主机B也会生成一个随机数据填充在序号区域,并把标志位SYN置为1,表示这是一个连接请求报文。


-
第三次握手
主机A接收到主机B发来的ACK + SYN请求时,首先判断ACK,表示主机B有应答能力,再去判断SYN,在序号的基础上+ 1,把结果填充在确认序号中,把标志位ACK置为1.

-
主机B接收到主机A发送来的ACK,表示主机A有应答能力,网络验证完成,建立连接成功。
1. 问:三次握手能不能变成两次或者四次?
答:四次可以,但是两次不行。两次就会少验证一方的应答能力。
2. 问:为什么第二次握手的ACK和SYN可以合并?答:在三次握手阶段,双方的连接还未建立,双方都还没有开始传输任何应用层数据,没有任何负担。
2.4.2 断开连接时,四次挥手

- 第一次挥手
主机A主动发起断开连接请求,给主机B发送一个TCP报文。该报文中会将主机B之前已经传送数据的最后一个字节的序列号加1填充到序列号中,并把标志位FIN置为1。

- 第二次挥手
主机B接收到主机A发送的FIN包之后,会将主机A发送的序列号 + 1 填充到确认序号中,并将标志位ACK置为1。

- 第三次挥手
当主机B也将剩余数据发送完毕之后,它会发送一个FIN包。该包的中会将标志位FIN置为1,并把主机A之前已传送数据的最后一个字节的序列号+ 1填充到该包的序号中。

- 第四次挥手
主机A收到主机B的FIN包后,发出确认包。该包会把标志位ACK置为1,并将主机B上一次发送到FIN包中的序号+ 1填充到确认序号中。

1. 四次挥手能合并成三次挥手吗?/第二次挥手和第三次挥手能合并吗?
答:大概率不能。
第二次挥手的ACK时操作系统级别做出的应答,是TCP协议本身的设计,和应用层无关。
第三次挥手的FIN是应用层去实现的,在发出FIN包之前需要回收一些资源之后才能触发。
2.5 滑动窗口(效率机制)

正常情况是发送一条数据等一个应答,应答中包含下次要发送的位置。

为了提升效率改为批量发送。窗口的大小指的是无需等待确认应答而可以继续发动数据的最大值。
发送方批量发送(根据当前窗口的大小发送报文),把正在发送的数据加入到缓冲区中,并记录最大字节数;接收方收到报文之后,返回一个ACK;发送方收到一个ACK之后,把缓冲区的数据删除椅子,然后再加入一组新的数据到缓冲区,继续发送。

窗口越大网络吞吐量就越大,效率越高,如果窗口无限大,就和UDP一样了。
2.5.1 异常情况
1. 数据报已抵达,ACK包丢了

图中的1001、3001、4001ACK是正常到达了主机A,但是主机A是正常接收到了6001的ACK,那么就代表6001前面所有的数据都传送成功了。
2. 数据包丢了,没有到达主机B

图中的是1001-2000的数据包丢失了,但是其他的数据包主机B都正常接收到了。
当某一段报文丢失了之后,接收端就会一直给发送端发送1001这样的ACK,发送端连续三次接收到同样的1001这样的ACK之后,就会将对应的1001-2000的数据重新发送。
此时,接收端重新正常接收到了1001的数据,就会给发送端发送7001的ACK。
这是因为之前正常接收的数据并不会被接收端舍弃,而是存放到接收端操作系统内核的接收缓冲区中。
2.6 流量控制(效率机制)
有了滑动窗口之后,提高了发送方是发送效率,并不是说发送方可以无限大为所欲为的发送数据了。
流量控制的作用就是用来通过接收方返回来的ACK进行反向控制送方的窗口大小。接收方会把自己能够处理的数据量主动告诉发送方,从而让发送方动态调整窗口的大小。

发送方在发送数据的时候,接收方会在自己的缓冲区中把接收到的数据按照编号组织好,等待着应用程序来读取。

如果接收端的缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,但是会定期发送一个窗口探测,使接收端把窗口大小告诉发送端。
2.7 拥塞控制(可靠机制)
作用:通过网络的畅通程度来控制窗口的大小。

发送数据时,由于会经过很多网络设置,网络情况也是不固定的。在数据传输的过程中会造成阻塞和超时。

-
程序启动的时候会把拥塞窗口的值调到很低,比如1.
-
如果接收到接收端的ACK,表示当前窗口没有问题,就调大窗口。
-
窗口到达一定的阈值之前会以指数的形式增大,到达阈值之后,以1为步长进行增大。
-
当增大到一定程度之后就会出现丢包的情况,就证明网络阻塞了,这时就把窗口重新调成1,同时重置新的阈值为当前窗口的二分之一。
拥塞窗口的大小和接收端ACK应答中的窗口大小共同决定滑动窗口的大小,二者取较小值。
2.8 延迟应答
TCP在应答时,并不是每次都应答,而是每隔几次就进行应答,可能是2次。
假设我们现在是2次应答一次,那么如果只要3条报文就结束了,那么还可以通过时间间隔来进行应答,一般是200ms。

延迟应答和及时应答是不同的,在应答时都需要带上窗口大小。及时应答的窗口大小是缓冲区的剩余空间。
延时应答时,应用程序会从缓冲区中取走一部分数据,这时缓冲区的剩余空间就变大到了,ACK时窗口大小就可以设置更大的值,最终发送方就会按照这个值去增大窗口。

2.9 捎带应答

当主机A发送响应数据时,如果有ACK需要返回,那么这两个报文就有可能被合并成一个。这样减少了通信次数,提升了效率。
ACK是系统内核做出的应答(传输层)
发送响应是应用程序层面的(应用层)
2.10 面向字节流
创建一个TCP的socket,同时在内核中创建一个发送缓冲区和一个接收缓冲区。
调用write时,数据会先写入发送缓冲区。如果发送的字节数太长,会被拆分成多个TCP的数据包发出;如果发送的字节数太短,会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去。
接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区,然后应用程序可以调用read从接收缓冲区拿数据。
2.11 粘包问题
粘包指的是应用层的数据包。

每接收到一个报文就把它放到缓冲区中,在读取报文时,不能有效的区分每个数据包的问题,称为粘包问题。
2.11.1 解决粘包问题一:定义一个分隔符
定义一个分隔符来界定消息之间的边界如果是换行符:\r\n
你好啊,一会儿去吃火锅\r\n行不行呀\r\n是不是在忙\r\nadflsjflsfj;lk;ds\r\n
2.11.2 解决粘包问题一:在应用层协议中定义一个区域,来表示当前消息的长度

2.12 异常情况
2.12.1 程序崩溃
系统会回收进程的资源,包括文件描述符表,回收时相当于调用socket的close(),触发FIN操作。
2.12.2 正常关机
处理方式和程序崩溃一样,都是正常断开连接。
2.12.3 主机掉电或断网
2.12.3.1 接收方断网
发送端接收不到接收端的ACK应答,会触发超时重传,多次重传之后发送端依旧没有收到ACK应答,会进行重置连接。如果连接重置也失败,只能放弃连接。

2.12.3.2 发送方断网
TCP自己也会定时发送心跳包,服务器定期扫描管理的连接,如果发现一个连接长时间没有发来心跳,就会主动丢弃这个连接。
3. 面试题:TCP和UDP协议的区别?
TCP:有连接、可靠传输、面向字节流、有接收缓冲区和发送缓冲区、大小不限、全双工。
UDP:无连接、不可靠传输、面向数据报、有接收缓冲区,无发送缓冲区、大小受限、全双工。
并详细讲述每一项。