TCP、UDP的区别
一、UDP
UDP的包头:UDP只有端口和数据。
UDP的特点:
①沟通简单,不需要大量的数据结构,处理逻辑和包头字段;
②不建立连接,只会监听,谁都可以发送或者接受数据;
③一直发送数据,不懂变通;
二、TCP
TCP的包头:①端口;②包的序号,解决乱序问题;③确认序号,确认对方收到的序号;④状态位,维护TCP连接;⑤窗口大小,做流量控制;⑥数据。
2.1TCP的三次握手
确保数据能够到达目标:
TCP 协议采用了三次握手(three-way handshaking)策略。
握手过程中使用了 TCP 的标志(flag) —— SYN(synchronize) 和 ACK(acknowledgement)。
发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后, 回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束。
为什么要有三次挥手?两次不行嘛?
假设,只有两次,客户端发送请求,服务端回复确认,建立连接;正常情况不会有问题。如果存在网络波动或者异常,客户端发送请求后,服务端没有收到,客户端没有收到回复则以为失败,重新发送请求建立连接。此时上一个请求如果之后后发送给服务端,则服务段立马建立连接回复客户端。但实际上,已经与客户端有连接了,该连接就是一个无用的浪费。加入第三次客户端的确认请求后,服务端发送的第二次的建立连接的请求,就会被客户端否则掉(通过ack码进行区别),那么客户端就不会发送确认请求,第二次连接就不会建立,避免了资源浪费。
2.2TCP的四次挥手
如何断开连接:
断开的时候,当 A 说不玩了,就进入 FIN_WAIT_1 的状态,B 收到 A 不玩了的消息后,进入 CLOSE_WAIT 的状态。
A 收到 B 说知道了,就进入 FIN_WAIT_2 的状态,如果 B 直接跑路,则 A 永远处与这个状态。TCP 协议里面并没有对这个状态的处理,但 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。
如果 B 没有跑路,A 接收到 B 的不玩了请求之后,从 FIN_WAIT_2 状态结束,按说 A 可以跑路了,但是如果 B 没有接收到 A 跑路的 ACK 呢,就再也接收不到了,所以这时候 A 需要等待一段时间,因为如果 B 没接收到 A 的 ACK 的话会重新发送给 A,所以 A 的等待时间需要足够长。
为什么要有四次挥手呢?
客户端第一次发送断开连接请求后,服务端回复确认。此时存在服务端有消息未发送的情况,客户端进去等待关闭状态,不发送请求,可以接受数据。服务端结束数据传送后,在通过一次服务端发起的请求确认,结束TCP连接。
2.3累计确认
实现可靠传输:
首先为了保证顺序性,每个包都有一个 ID。在建立连接的时候会商定起始 ID 是什么,然后按照 ID 一个个发送,为了保证不丢包,需要对发送的包都要进行应答,当然,这个应答不是一个一个来的,而是会应答某个之前的 ID,表示都收到了,这种模式成为累计应答或累计确认。
为了记录所有发送的包和接收的包,TCP 需要发送端和接收端分别来缓存这些记录,发送端的缓存里是按照包的 ID 一个个排列,根据处理的情况分成四个部分
- 发送并且确认的
- 发送尚未确认的
- 没有发送等待发送的
- 没有发送并且暂时不会发送的
对于接收端来讲,它的缓存里面的内容要简单一些
- 接收并且确认过的
- 还没接收,但是马上就能接收的
- 还没接收,但也无法接收的
2.4顺序和丢包问题
确认与重传机制
2.5拥塞控制问题
通过窗口的大小来控制的,但是检测网络满不满是个挺难的事情,所以 TCP 发送包经常被比喻成往谁管理灌水,所以拥塞控制就是在不堵塞,不丢包的情况下尽可能的发挥带宽。
水管有粗细,网络有带宽,即每秒钟能发送多少数据;水管有长度,端到端有时延。理想状态下,水管里面的水 = 水管粗细 * 水管长度。对于网络上,通道的容量 = 带宽 * 往返时延。
如图所示,假设往返时间为 8 秒,去 4 秒,回 4 秒,每秒发送一个包,已经过去了 8 秒,则 8 个包都发出去了,其中前四个已经到达接收端,但是 ACK 还没返回,不能算发送成功,5-8 后四个包还在路上,还没被接收,这个时候,管道正好撑满,在发送端,已发送未确认的 8 个包,正好等于带宽,也即每秒发送一个包,也即每秒发送一个包,乘以来回时间 8 秒。
TCP 拥塞控制主要来避免两种现象,包丢失和超时重传,一旦出现了这些现象说明发送的太快了,要慢一点。