1.初步认识
udp属于传输层的协议,适用于更注重传输速度和实时性,并能容忍一定程度数据丢失的场景
以下是UDP的特征
特性 |
描述 |
---|---|
无连接 |
发送数据前不需要建立连接,直接发送,减少了延迟和开销 |
不可靠传输 |
不保证数据包一定到达、不保证顺序、不提供重传机制 |
面向数据报 |
应用层交给UDP多长的报文,UDP就发送多少,保持报文边界 |
高效 |
头部开销小(仅8字节) ,没有拥塞控制,传输效率高 |
支持多播广播 |
支持一对一、一对多、多对多的通信方式 |
主要应用在以下场景:
•实时音视频传输:如视频会议、直播(VoIP)。少量丢包对体验影响不如延迟大。
•在线游戏:游戏状态更新对实时性要求极高。
•域名解析(DNS):查询请求小且需要快速响应。
•物联网(IoT)通信:许多传感器数据上报频率高,数据量小,且网络环境可能简单。
2.UDP协议的格式
UDP报文由首部和数据两部分组成,其中首部只有8个字节,包含四个字段:
源端口号(16位):发送方端口号
目的端口号(16位):接受方端口号
长度(16位):指示整个UDP数据报(首部+数据)的长度
校验和(16位):用于检查数据在传输过程中是否出错(IPv4可选,IPv6必须)
3.UDP使用示例
UDP接受端
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // close()
int main() {
// 1. 创建 UDP Socket
int server_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (server_socket < 0) {
std::cerr << "创建Socket失败!" << std::endl;
return 1;
}
// 2. 绑定服务器地址和端口
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr)); // 清空结构体
server_addr.sin_family = AF_INET; // IPv4
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
server_addr.sin_port = htons(12345); // 监听端口12345 (htons: 主机字节序转网络字节序)
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
std::cerr << "绑定端口失败!" << std::endl;
close(server_socket);
return 1;
}
std::cout << "UDP服务器已启动,正在端口 12345 监听..." << std::endl;
// 3. 准备接收数据
char buffer[1024]; // 接收缓冲区
sockaddr_in client_addr; // 用于存储客户端地址
socklen_t client_addr_len = sizeof(client_addr);
while (true) {
// 清空缓冲区
memset(buffer, 0, sizeof(buffer));
// 阻塞等待接收数据 (recvfrom)
int bytes_received = recvfrom(
server_socket,
buffer,
sizeof(buffer) - 1, // 留一个位置给字符串结束符 '\0'
0,
(sockaddr*)&client_addr,
&client_addr_len
);
if (bytes_received < 0) {
std::cerr << "接收数据出错!" << std::endl;
continue; // 继续监听
}
// 打印收到的消息和客户端信息
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
std::cout << "收到来自 [" << client_ip << ":" << ntohs(client_addr.sin_port) << "] 的消息: "
<< buffer << std::endl;
// 4. (可选) 发送回复
const char* reply = "服务器已收到你的消息!";
sendto(
server_socket,
reply,
strlen(reply),
0,
(sockaddr*)&client_addr,
client_addr_len
);
}
// 理论上不会执行到这里 (循环是 while true)
close(server_socket);
return 0;
}
UDP发送端
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> // close()
int main() {
// 1. 创建 UDP Socket
int client_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (client_socket < 0) {
std::cerr << "创建Socket失败!" << std::endl;
return 1;
}
// 2. 设置目标服务器地址
sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(12345); // 服务器端口
// 将IP地址字符串转换为网络格式 (127.0.0.1 是本机回环地址)
if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
std::cerr << "无效的服务器地址!" << std::endl;
close(client_socket);
return 1;
}
// 3. 发送数据
const char* message = "你好,UDP服务器!";
int bytes_sent = sendto(
client_socket,
message,
strlen(message),
0,
(sockaddr*)&server_addr,
sizeof(server_addr)
);
if (bytes_sent < 0) {
std::cerr << "发送失败!" << std::endl;
close(client_socket);
return 1;
}
std::cout << "已发送消息: " << message << std::endl;
// 4. (可选) 等待并接收服务器的回复
char buffer[1024];
sockaddr_in from_addr;
socklen_t from_addr_len = sizeof(from_addr);
memset(buffer, 0, sizeof(buffer));
int bytes_received = recvfrom(
client_socket,
buffer,
sizeof(buffer) - 1,
0,
(sockaddr*)&from_addr,
&from_addr_len
);
if (bytes_received < 0) {
std::cerr << "接收回复失败!" << std::endl;
} else {
std::cout << "收到服务器回复: " << buffer << std::endl;
}
// 5. 关闭Socket
close(client_socket);
return 0;
}