Linux 软件编程(十)网络编程:网络协议,UDP 与 TCP 知识点
一、UDP 协议
1. UDP 的核心特点
- 面向数据包:数据以独立数据包的形式传输,每个数据包包含完整的源地址和目的地址
- 无需建立连接:通信前不需要像 TCP 那样进行握手过程,直接发送数据
- 尽最大努力交付:不保证数据的可靠传输,可能出现丢包、乱序等情况
- 支持多种通信模式:可实现一对一、一对多的传输方式
- 高效轻量:机制简单,资源开销小,数据实时性高(适用于 VNC、直播等场景)
2. 如何避免 UDP 丢包
尽管 UDP 本身不保证可靠性,但可通过以下方式减少丢包:
- 控制发送速率,让接收方有足够时间处理数据
- 实现类似 TCP 的应答机制,接收方收到数据后向发送方确认
- 合理设置接收缓冲区大小,避免缓冲区溢出导致丢包
3. 网络抓包工具:Wireshark
Wireshark 是分析网络协议的重要工具,使用步骤如下:
- 通过
sudo wireshark
命令启动工具 - 选择需要抓取的网卡(通常选择 "any" 捕获所有网卡)
- 设置过滤条件(如
udp
或tcp
过滤特定协议) - 点击开始抓包按钮
- 进行目标网络通信,观察捕获的数据包详情
4. UDP 报文头部结构
UDP 头部固定为 8 字节,包含以下字段:
- 源端口号:发送方网络进程的端口号
- 目标端口号:接收方网络进程的端口号
- 长度:UDP 报文的总长度(头部 + 数据部分)
- 校验和:用于数据差错校验的字段
二、TCP 协议
TCP(传输控制协议)属于传输层协议,采用流式套接字,与 UDP 有显著区别。
1. TCP 的核心特点
- 面向数据流:数据以字节流形式传输,无数据包边界
- 面向连接:通信前必须建立连接(三次握手)
- 安全可靠:通过确认机制、重传机制等保证数据可靠传输
- 机制复杂:相比 UDP,网络资源开销较大
- 通信模式:本质上是一对一通信,可通过并发方式实现一对多通信
2. TCP 三次握手与四次挥手
三次握手(建立连接)
TCP 建立连接时需进行三次握手,确保收发双方都已准备就绪:
- 客户端发送带 SYN(请求建立连接)标志的报文
- 服务端返回带 SYN+ACK(同意连接 + 确认)标志的报文
- 客户端发送带 ACK(确认)标志的报文,连接建立
四次挥手(断开连接)
TCP 断开连接时需进行四次挥手(),确保双方数据都已传输完毕:
- 四次挥手:因为服务端可能存在 “未发完的数据”,需要拆分 “确认断开请求” 和 “真正断开连接” 两个步骤,保证数据不丢失、连接关闭有序。
- 主动方发送带 FIN(请求断开)标志的报文
- 被动方返回 ACK(确认)标志的报文
- 被动方发送带 FIN(请求断开)标志的报文
- 主动方返回 ACK(确认)标志的报文,连接关闭
3. TCP 编程流程
理解:客户端通过
connect()
发起连接请求,服务端通过listen()
监听,accept()
接收完成 三次握手的客户端
客户端流程
socket()
:创建套接字connect()
:请求与服务端建立连接send()
/recv()
:发送和接收数据close()
:关闭连接
服务端流程
socket()
:创建监听套接字(sockfd)bind()
:绑定 IP 地址和端口listen()
:监听客户端连接请求accept()
:接收连接,生成通信套接字(connfd)recv()
/send()
:通过通信套接字收发数据close()
:关闭连接
关键函数解析
// 建立连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:请求与服务端建立连接 参数:sockfd:套接字addr:要连接的服务端的地址信息addrlen:服务端地址大小返回值:成功:0失败:-1//发送数据
ssize_t send(int sockfd, const void *buf, size_t len, int flags);功能:发送网络数据参数:sockfd:网络套接字buf:要发送的数据首地址len:发送的字节数flags:0 :按照默认方式发送返回值:成功:实际发送的字节数失败:-1// 监听连接
int listen(int sockfd, int backlog);功能:监听建立三次握手的客户端参数:sockfd:监听套接字backlog:最大允许监听的客户端个数返回值:成功:0失败:-1// 接收连接
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); 功能:接收建立三次握手的客户端,并产生一个通讯套接字参数:socket:监听套接字address:客户端的地址信息address_len:客户端地址长的指针返回值:成功:通讯套接字失败:-1// 接收数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);功能:从网络套接字上接收数据参数:sockfd:通讯套接字buf:存放接收数据的首地址len:期望接收到的字节数flag : 0:默认方式接收(阻塞)返回值:成功:实际接收到的字节数失败:-1对方断开连接:0
4. TCP 粘包问题及解决方案
粘包原因
- 发送方速度快,TCP 底层对多包数据重新组帧
- 接收方处理速度慢,多包数据在缓冲区堆积,一次读出
解决方法
- 控制发送速率:避免发送过快导致粘包(可以加入usleep(1)函数, 让发送方晚一点)
- 固定数据大小:发送和接收固定大小的数据(如结构体),注意跨平台(32/64位系统)对齐问题(32位:long为4字节;64位:long为8字节)
- 添加分隔符:在数据末尾添加特定分隔符(如
\n
),接收方根据分隔符解析 - 自定义协议帧:设计包含帧头、帧尾、长度、校验等字段的自定义协议(如:
AA [长度] [数据] [校验] BB
)
三、总结
UDP 和 TCP 的核心区别
- 连接性:UDP 无连接,TCP 有连接
- 可靠性:UDP 不可靠,TCP 可靠
- 数据形式:UDP 面向数据包,TCP 面向数据流
- 开销:UDP 开销小,TCP 开销大
- 适用场景:UDP 适用于实时通信,TCP 适用于可靠传输
TCP 三次握手和四次挥手的意义
- 三次握手:确保双方收发能力正常,避免无效连接
- 四次挥手:保证双方都已完成数据传输,避免数据丢失
TCP 粘包问题的本质
- 源于 TCP 的流式传输特性,没有数据包边界,需在应用层解决
TCP 编程的核心要点
- 服务端需区分监听套接字和通信套接字
- 注意处理
recv()
返回 0 的情况(表示对方断开连接) - 合理设置
listen()
的 backlog 参数,控制并发连接数