一、TCP

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

1、TCP头格式

1、序列号:用来解决乱序问题,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。

2、确认应答号:用来解决丢包问题,指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。

3、控制位:

  • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。
  • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
  • SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
  • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

二、UDP

TCP 是面向无连接的、不可靠的、基于报文的传输层通信协议。

1、头格式

三、TCP和UDP区别

1. 连接

  • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
  • UDP 是不需要连接,即刻传输数据。

2. 服务对象

  • TCP 是一对一的两点服务,即一条连接只有两个端点。
  • UDP 支持一对一、一对多、多对多的交互通信

3. 可靠性

  • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。
  • UDP 是尽最大努力交付,不保证可靠交付数据。

4. 拥塞控制、流量控制

  • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
  • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

5. 首部开销

  • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
  • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

6. 传输方式

  • TCP 是流式传输,没有边界,但保证顺序和可靠。
  • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

7. 分片不同

  • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
  • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。

四、TCP三次握手

 

1、一开始客户端和服务端都处于close状态,先是服务器主动监听某个端口,处于listen状态

2、客户端随机初始化序列号并发送SYN报文,之后客户端处于SYN-SENT 状态

3、服务端接收到报文后,也随机初始化自己的序列号,并将TCP首部的确认应答号填入客户端发来的序列号+1,然后发送SYN+ACK报文,之后处于SYN-RCVD 状态

4、客户端收到报文后,向服务端返回ACK报文,确认应答号是服务端发过来的序列号+1,之后处于ESTABLISHED 状态

5、服务端收到ACK报文后,也进入ESTABLISHED 状态

 

为什么是三次握手?

三次握手才能保证双方具有接收和发送的能力。

 

四、TCP四次挥手

1、客户端发送FIN报文,之后进入FIN_WAIT_1 状态

2、服务端接收报文后发送ACK报文,服务端进入CLOSE_WAIT 状态

3、客户端收到ACK报文后进入FIN_WAIT_2 状态

4、等服务器处理完数据后发送FIN报文,之后服务端进入 LAST_ACK 状态。

5、客户端收到报文后发送ACK报文,进入TIME_WAIT 状态

6、服务端收到ACK报文后,进入CLOSE 状态,至此服务端已经完成连接的关闭。

7、客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

 为什么要四次挥手 ?

客户端发起结束连接请求后,服务端有可能还有数据要发送,当服务端没有数据发送时,才可以断开连接。

为什么要等待2MSL再关闭连接 ?

MSL是报文最大生存时间

在客户端发送对服务端的FIN确认包ACK后,这个ACK包有可能到达不了,所以客户端发送ACK后需要留出 2MSL时间(ACK到达服务器器+服务器发送FIN重传包,一来一回)等待确认服务器端确实收到了ACK包。也 就是说客户端如果等待2MSL时间也没收到服务器端重传的FIN包,则就可以确认服务器已经收到客户端发送 的ACK包,则结束TCP连接。

 

五、TCP保证可靠性

TCP 是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。

1、重传机制

  • 超时重传
  • 快速重传
  • SACK
  • D-SACK

超时重传:在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。

快速重传:当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

 

2、滑动窗口

窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。

发送方的滑动窗口:

 接收方的滑动窗口:

 

3、流量控制

TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。

让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制,发送方就会根据接收方传来的ACK报文缩小窗口。

 

4、拥塞控制

避免「发送方」的数据填满整个网络。

4.1 拥塞窗口

拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。

我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。

拥塞窗口 cwnd 变化的规则:

  • 只要网络中没有出现拥塞,cwnd 就会增大;
  • 但网络中出现了拥塞,cwnd 就减少;

其实只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。

拥塞控制主要是四个算法:

  • 慢启动
  • 拥塞避免
  • 快重传
  • 快恢复

慢启动:每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1,慢启动的发包个数是指数性的增长。直到达到慢启动门限  ssthresh。

  • 当 cwnd < ssthresh 时,使用慢启动算法。
  • 当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。

拥塞避免:每当收到一个 ACK 时,cwnd 增加 1/cwnd,发包个数是线性增长。

快重传:当网络拥塞时发生了数据包的丢失,这时候使用快重传算法,收到3个相同的ACK包就重新发送。

ssthresh 和 cwnd 变化如下:

  • cwnd = cwnd/2 ,也就是设置为原来的一半;
  • ssthresh = cwnd;
  • 进入快速恢复算法

快恢复:不会突然减少数据流,否则会造成网络卡顿

  • 拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
  • 重传丢失的数据包;
  • 如果再收到重复的 ACK,那么 cwnd 增加 1;
  • 如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;