UDP 通信详解:sendto
和 recvfrom
的使用
1. 概述
UDP(User Datagram Protocol)是一种无连接、不可靠的传输层协议,适用于对实时性要求高但允许少量丢包的场景(如视频流、DNS 查询)。与 TCP 不同,UDP 不需要建立连接,直接通过 sendto
和 recvfrom
发送和接收数据。
本文将详细介绍:
• sendto
和 recvfrom
的函数原型及参数
• UDP 通信的基本流程
• 完整的服务器端和客户端代码示例
2. sendto
函数
sendto
用于通过 UDP 套接字 发送数据。
函数原型
#include <sys/socket.h>
ssize_t sendto(
int sockfd, // 套接字文件描述符
const void *buf, // 待发送数据的缓冲区
size_t len, // 数据长度
int flags, // 标志位(通常设为 0)
const struct sockaddr *dest_addr, // 目标地址
socklen_t addrlen // 目标地址长度
);
参数说明
参数 | 说明 |
---|---|
sockfd |
已创建的 UDP 套接字 |
buf |
指向待发送数据的缓冲区 |
len |
数据长度(字节数) |
flags |
控制发送行为(通常设为 0 ) |
dest_addr |
目标地址(struct sockaddr_in 或 struct sockaddr ) |
addrlen |
目标地址结构体的大小 |
返回值
• 成功:返回实际发送的字节数
• 失败:返回 -1
,并设置 errno
示例
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888); // 目标端口
inet_aton("127.0.0.1", &server_addr.sin_addr); // 目标 IP
char *msg = "Hello, UDP Server!";
sendto(sockfd, msg, strlen(msg), 0,
(struct sockaddr *)&server_addr, sizeof(server_addr));
3. recvfrom
函数
recvfrom
用于通过 UDP 套接字 接收数据。
函数原型
#include <sys/socket.h>
ssize_t recvfrom(
int sockfd, // 套接字文件描述符
void *buf, // 接收数据的缓冲区
size_t len, // 缓冲区最大长度
int flags, // 标志位(通常设为 0)
struct sockaddr *src_addr, // 存储发送方地址
socklen_t *addrlen // 指向地址长度的指针
);
参数说明
参数 | 说明 |
---|---|
sockfd |
已绑定的 UDP 套接字 |
buf |
接收数据的缓冲区 |
len |
缓冲区最大长度 |
flags |
控制接收行为(通常设为 0 ) |
src_addr |
存储发送方的地址信息(NULL 表示不关心) |
addrlen |
指向 src_addr 长度的指针 |
返回值
• 成功:返回接收的字节数
• 失败:返回 -1
,并设置 errno
示例
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
char buf[1024];
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&client_addr, &addr_len);
if (recv_len > 0) {
printf("Received from %s:%d: %s\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),
buf);
}
4. UDP 通信流程
UDP 通信的基本流程如下:
服务器端:
• 创建套接字(socket
)• 绑定地址(
bind
)• 接收数据(
recvfrom
)• 发送数据(
sendto
)客户端:
• 创建套接字(socket
)• 发送数据(
sendto
)• 接收数据(
recvfrom
)
5. 完整代码示例
5.1 服务器端代码
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <IP> <PORT>\n", argv[0]);
exit(EXIT_FAILURE);
}
// 1. 创建 UDP 套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 2. 绑定地址
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
if (inet_aton(argv[1], &addr.sin_addr) == 0) {
fprintf(stderr, "Invalid IP address\n");
exit(EXIT_FAILURE);
}
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
printf("Server running on %s:%s\n", argv[1], argv[2]);
// 3. 接收数据
char buf[1024];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
while (1) {
memset(buf, 0, sizeof(buf));
ssize_t recv_len = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&client_addr, &addr_len);
if (recv_len > 0) {
printf("Received from %s:%d: %s\n",
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port),
buf);
}
}
close(sockfd);
return 0;
}
5.2 客户端代码
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr, "Usage: %s <IP> <PORT>\n", argv[0]);
exit(EXIT_FAILURE);
}
// 1. 创建 UDP 套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 2. 设置目标地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
if (inet_aton(argv[1], &server_addr.sin_addr) == 0) {
fprintf(stderr, "Invalid IP address\n");
exit(EXIT_FAILURE);
}
// 3. 发送数据
char buf[1024];
while (1) {
printf("Input message: ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1] = '\0'; // 去掉换行符
sendto(sockfd, buf, strlen(buf), 0,
(struct sockaddr *)&server_addr, sizeof(server_addr));
}
close(sockfd);
return 0;
}