【TCP/IP】TCP和UDP的原理与区别
简介
TCP 协议的作用
因为 IP 层协议是不可靠的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
-
面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
-
可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
-
字节流:用户消息通过 TCP 协议传输时,消息可能会被操作系统「分组」成多个的 TCP 报文,如果接收方的程序如果不知道「消息的边界」,是无法读出一个有效的用户消息的。并且 TCP 报文是「有序的」,当「前一个」TCP 报文没有收到的时候,即使它先收到了后面的 TCP 报文,那么也不能扔给应用层去处理,同时对「重复」的 TCP 报文会自动丢弃。
TCP 连接
Connections: The reliability and flow control mechanisms described above require that TCPs initialize and maintain certain status information for each data stream. The combination of this information, including sockets, sequence numbers, and window sizes, is called a connection.
--摘自 RFC 793
简而言之,TCP 连接是用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。
建立一个 TCP 连接是需要客户端与服务端达成如下三个信息的共识:
-
Socket:由 IP 地址和端口号组成
-
序列号:用来解决乱序问题等
-
窗口大小:用来做流量控制
TCP 四元组
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
- 源地址
- 源端口
- 目的地址
- 目的端口
服务器支持的最大连接数
通常,我们会思考这样一个问题:有一个 IP 的 Linux 服务端监听了一个端口,那么,它能支持的最大 TCP 连接数是多少呢?
服务端通常固定在某个本地端口上监听,等待客户端的连接请求。因此,客户端 IP 和端口是可变的,其理论值计算公式如下:
理论上来说,对于 IPv4 协议,客户端的 IP 数最多为 \(2^{32}\),客户端的端口数最多为 \(2^{16}\),那么,服务端单机理论最大的 TCP 连接数约为 \(2^{48}\) 。
当然,服务端最大并发 TCP 连接数远不能达到理论上限,会受以下因素影响:
-
文件描述符限制:每个 TCP 连接都是一个文件,如果文件描述符被占满了,会发生 Too many open files。Linux 对可打开的文件描述符的数量分别作了三个方面的限制:
-
系统级:当前系统可打开的最大数量,通过 cat /proc/sys/fs/file-max 查看;
-
用户级:指定用户可打开的最大数量,通过 cat /etc/security/limits.conf 查看;
-
进程级:单个进程可打开的最大数量,通过 cat /proc/sys/fs/nr_open 查看;
-
-
内存限制:每个 TCP 连接都要占用一定内存,操作系统的内存是有限的,如果内存资源被占满后,会发生 OOM。
TCP 报文头格式
TCP 报文头的格式如下:
其中,比较重要的几个字段如下:
-
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
-
确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
-
控制位:
-
ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 ;
-
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接;
-
SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定;
-
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
-
UDP 报文头格式
头部只有 8 个字节(64 位),UDP 的头部格式如下:
字段含义:
-
源端口和目标源端口:主要是告诉 UDP 协议应该把报文发给哪个进程;
-
包长度:该字段保存了 UDP 首部的长度跟数据的长度之和;
-
校验和:校验和是为了提供可靠的 UDP 首部和数据而设计,防止收到在网络传输中受损的 UDP 包。
TCP 与 UDP 的差异
TCP 和 UDP 区别:
-
连接
-
TCP 是面向连接的传输层协议,传输数据前先要建立连接。
-
UDP 是不需要连接,即刻传输数据。
-
-
服务对象
-
TCP 是一对一的两点服务,即一条连接只有两个端点。
-
UDP 支持一对一、一对多、多对多的交互通信。
-
-
可靠性
-
TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。
-
UDP 是尽最大努力交付,不保证可靠交付数据。但是我们可以基于 UDP 传输协议实现一个可靠的传输协议,比如 QUIC 协议,可以参考:如何基于 UDP 协议实现可靠传输?
-
-
拥塞控制、流量控制
-
TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
-
UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
-
-
首部开销
-
TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
-
UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
-
-
传输方式
-
TCP 是流式传输,没有边界,但保证顺序和可靠。
-
UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
-
-
分片不同
-
TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
-
UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。
-
TCP 和 UDP 应用场景
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
-
FTP 文件传输;
-
HTTP / HTTPS;
由于 UDP 面向无连接,它可以随时发送数据,再加上 UDP 本身的处理既简单又高效,因此经常用于:
-
包总量较少的通信,如 DNS 、SNMP 等;
-
视频、音频等多媒体通信;
-
广播通信;
TCP的首部长度和UDP的包长度
TCP的首部长度
为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有首部长度字段呢?
主要是因为,TCP 有可变长的「选项」字段,而 UDP 头部长度固定的(8个字节),无需多一个字段去记录 UDP 的首部长度。
UDP的包长度
为什么 UDP 头部有包长度字段,而 TCP 头部则没有「包长度」字段呢?
我们先看 TCP 报文是如何计算负载数据长度:
其中,IP 报文总长度 和 IP 首部长度,在 IP 首部格式是已知的。TCP 首部长度,则是在 TCP 首部格式已知的,所以就可以求得 TCP 数据的长度。
其实 UDP的长度,可以通过类似的公式计算得到:
UDP Data的长度 = IP总长度 - IP Header长度 - UDP Header长度
这样看来,感觉 UDP 的「包长度」是冗余的,但是,UDP的包长度存在的主要原因如下:
-
IP 层是网络层的,而 UDP 是传输层的
数据到了传输层,数据包就已经不存在IP头信息了,那么,此时的 UDP 数据会被放在 UDP 的 Socket Buffer 中。当应用层来不及取这个 UDP 数据报,那么,两个数据报在数据层面其实都是一堆 01 串。
此时,读取第一个数据报的时候,会先读取到 UDP 头部,如果此时 UDP 头不含 UDP 长度信息,那么,应用层应该取多少数据才算完整的一个数据报呢?
因此,UDP 头的这个长度,其实跟 TCP 为了防止粘包而在消息体里加入的边界信息是起一样的作用的,如下图所示:
-
UDP 协议与 IP 协议并行发展的结果
UDP的传输依赖的可能是其他不提供自身报文长度或首部长度的网络层协议,因此 UDP 报文首部需要有长度字段以供计算。
总结
总结:
-
UDP包含长度字段可以确认边界,从而解决粘包半包的问题,因此提供的是“数据报”服务;
-
TCP没有长度字段,所以提供的是“流式”服务,需要在应用层提供边界处理解决这个问题,相当于把“确认边界”的问题保留到上层解决,但也正因为这个特性,使得TCP可以以字节为单位提供流水线服务。
参考: