本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
前言
接上篇TCP相关知识汇总-P1,学习掘金小册深入理解 TCP 协议:从原理到实战的笔记
TCP&UDP对比总览
TCP和UDP都是传输层的协议,但二者具有不同的特性,也适用于不同的场景。如下表所示:
TCP | UDP | |
|---|---|---|
| 可靠性 | 可靠 | 不可靠 |
| 连接性 | 面向连接 | 无连接 |
数据经过传输之后可能是无序的,TCP协议会将这些无序的数据重新进行组装成有序的,供给上层调用者使用 | 无序 | |
| 报文 | 面向字节流 | 面向报文 |
| 效率 | 传输效率低 | 传输效率高 |
| 双工性 | 全双工 | 一对一、一对多、多对一、多对多 |
| 流量控制 | 滑动窗口 | 无 |
| 拥塞控制 | 慢开始、拥塞避免、快重传、快恢复 | 无 |
| 无,若是接收端缓存区足够大,可将发送端多次发送的数据一次性接收,然后传给上层应用 | 有,发送端发送一次,接收端就要接收一次,发送多少次就要接收多少次 | |
| 传输速度 | 慢 | 快 |
| 重量级,数据报报头大小为20个字节 | 轻量级,数据报报头大小为8个字节 | |
| 应用场景 | 对效率要求低、对可靠性要求高或者要求有连接的场景,如文件传输,邮件发送等 | 对效率要求高、对准确性要求低,如即时通信,QQ,视频通话等 |
基于连接 vs 无连接
TCP是面向连接的协议,UDP是无连接的协议。因而,当使用TCP协议传输数据时,客户端与服务端之间必须通过三次握手来建立连接。
可靠性
TCP提供交付保证,有许多机制用于保证消息的可靠性。
- 校验和:每个报文的报头部分都有一个校验和,防止在传输途中数据被损坏。如果收到一个校验和有差错的报文,
TCP直接丢弃,不会进行确认; - 序列号:每个报文的报头部分都有一个序列号,借助于这个序列号,可以将不同报文中的数据重新按照顺序进行组装,从而保证消息的有序性;
- 超时重传:发送完消息之后,
TCP会启动一个定时器,等待对端确认这个数据包。如果在指定时间内没有确认,则会进行重传,再等待一段时间,往复几次,直到重传次数超过一定次数之后,就会丢弃这个包; - 流量控制和拥塞控制:下面会详细介绍到。
有序性
TCP协议会根据报头的序列号将传输过来的无序数据整理成有序的,而UDP不会。
效率
TCP比较慢,而UDP比较快。因为TCP必须要先建立连接,以保证消息的可靠性和有序性,需要进行的内部操作比UDP多很多。TCP适合大量数据的传输,UDP适合少量数据的传输。
量级
TCP是重量级协议,而UDP是轻量级协议。一个TCP数据报的报头至少为20个字节,UDP数据报报头固定是8个字节。如下所示:
报文
TCP是面向字节流的协议,无边界记录。而UDP发送的每个数据是记录型的数据报,所谓记录型数据报就是接收进程可以识别到接收到的数据报的记录边界。
那么问题来了,TCP提供了一种字节流服务,而收发双方都不保持记录的边界,应用程序应该如何提供他们自己的记录标识呢?
因为发送窗口(接收主机能够接收的数据量)、拥塞窗口(对网络拥塞的估计)、路径上的最大传输单元(传输的最大数据量)以及慢启动等等因素。所以不能确定TCP的分包个数与大小。为了解决这个问题,使用者可采取下面两种方式
- 定长报文,读取报文中的固定字节;
- 可以用结束标记(回车、换行)来分隔记录;
TCP报头分析
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP 报文段的报头有 10 个必需的字段和 1 个可选字段。报头至少为 20 字节。报头后面的数据是可选项。
源端口和目的端口
各占2个字节字节,分别是源端口号和目的端口号
序列号
4个字节,使用mod计算,TCP协议是面向字节流的,在TCP连接中传输的字节流的每一个字节都是按照顺序编号的。
确认号
4个字节,表示期望收到对方下一个报文段的第一个数据字节的序号。若确认号为N,则表示到序号N-1为止的所有数据都已经确认收到。
数据偏移
4位,TCP报文段的数据起始处距离TCP报文段的起始处有多远,即首部长度。 由于 TCP 报头的长度随 TCP 选项字段内容的不同而变化,因此报头中包含一个指定报头字段的字段。TCP报头最小20个字节,最大60个字节。
保留
6位,目前还未使用,待以后使用,目前都是0。
控制位
6位
URG
当URG=1时表示紧急指针字段有效。这个时候发送方TCP就会把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。
ACK
当ACK=1时,确认字段有效。当ACK=0,确认字段无效。**TCP规定,在连接建立后所有传送的报文段都必须置为1。 **
PUSH
接收方TCP收到PUSH=1的报文段时,就会尽快交付给上层应用程序,而不是等到整个缓存区都填满了之后再交付。
RST
当RST=1时,表明TCP连接出现严重差错,必须释放连接,然后再重新建立连接。(先释放连接,再重新建立连接)。
SYN
在建立连接时用来同步序号。当SYN=1,ACK=0时,表明这是一个用来建立连接的请求报文。对方若是同意建立连接,则会回应一个SYN=1,ACK=1的报文。故SYN=1,表明这是一个连接请求或者是连接接收报文。
FIN
用于释放连接。当FIN=1时,表明发送方的数据已发送完毕,并要求释放连接。
窗口
2个字节,此字段用来进行流量控制,这个值是本机期望一次接收的字节数,告诉对方在不等待确认的情况下,可以发来多大的数据。这里表示的最大长度是2^16 - 1 =65535,如需要使用更大的窗口大小,需要使用选项中的窗口扩大因子选项。指发送本报文段的一方的接收窗口(而不是自己的发送窗口)。
校验和
源主机和目的主机根据TCP报文段以及伪报头的内容计算校验和。伪报头中存放着来自IP报头以及TCP报文段长度信息。与UDP一样,伪报头并不在网络中传输,并且在校验和中包含伪报头的目的是为了防止目的主机错误地接收存在路由的错误数据报。
伪首部
伪首部,又称伪报头:指在TCP的分段或者是UDP的数据报格式中,在数据报首部增加,源IP地址,目的IP地址,IP分组协议字段,TCP或者UDP数据报的总长度,构成的扩展首部结构,共12个字节。伪首部是个临时结构,不向下也不向上传递,仅仅是为了保证校验套接字的正确性。
其数据结构如下所示:
//伪头部:用于TCP/UDP计算CheckSum
//填充字段值来自IP层
typedef struct tag_pseudo_header
{
u_int32_t source_address; //源IP地址
u_int32_t dest_address; //目的IP地址
u_int8_t placeholder; //必须置0,用于填充对齐
u_int8_t protocol; //协议号(IPPROTO_TCP=6,IPPROTO_UDP=17)
u_int16_t tcplength; //UDP/TCP头长度(不包含数据部分)
}PseudoHeader_S;
伪首部的结构示意图如下所示:
紧急指针
仅在 URG = 1 时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据),即指出了紧急数据的末尾在报文中的位置,注意:即使窗口为零时也可发送紧急数据。例如,**如果报文段的序号是 1000,前 8 个字节都是紧急数据,那么紧急指针就是 8 **。紧急指针一般用途是使用户可中止进程。
可选项
可选项的格式如下所示:
常用的可选项有以下几种:
timestamp时间戳MSS允许接收的最大报文段SACK选择确认选项Window Scale窗口缩放因子
可选项的长度可变,最长可达 40 字节,当没有使用可选项时,TCP 首部长度是 20 字节。
填充
用于保证选项大小为32位的整数倍。
数据
真正需要进行传输的数据。
UDP报头分析
UDP 是 User Datagram Protocol 的简称, 中文名是用户数据报协议,是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
源端口
2个字节,发送方端口号
目的端口号
2个字节,接收方端口号
报文长度
2个字节,UDP用户数据报的总长度,以字节为单位。
校验和
2个字节,检测UDP用户数据报在传输过程中是否出错,出错直接丢弃。
伪首部
数据
真正需要传输的数据。需要注意的是,UDP 的数据部分如果不为偶数需要用 0 填补,就是说,如果数据长度为奇数,数据长度加“1”。
常见问题
TCP 比 UDP 安全,为什么还要用UDP?
- 无需建立连接(延迟小);
- 无需维护连接状态;
- 头部开销小,固定8个字节;
- 应用层可以更好地控制要发送的数据和发送时间。
UDP为什么那么快?
- 不需要建立连接;
- 不需要进行确认;
- 没有超时重发机制;
- 没有流量控制和拥塞控制;