Linux 下 UDP 编程入门指南
UDP(用户数据报协议)是一种无连接的传输层协议,它提供了一种简单、快速但不可靠的数据传输方式。与 TCP 不同,UDP 不建立连接,也不保证数据包的顺序和可靠性,但它具有较低的开销和更高的传输速度,适用于对实时性要求较高但对数据丢失容忍度较高的应用场景,例如流媒体、在线游戏和 DNS 查询等。
本文将详细介绍 Linux 下 UDP 编程的基本概念、API 函数以及一个完整的示例程序,帮助读者快速入门 UDP 编程。
1. UDP 编程基础
UDP 编程的核心在于使用套接字(socket)进行数据传输。套接字是一种抽象的通信端点,它允许进程之间通过网络进行通信。在 UDP 编程中,我们需要创建一个 UDP 套接字,然后使用该套接字发送和接收数据。
1.1 地址结构
UDP 使用 IP 地址和端口号来标识通信的双方。IP 地址用于标识网络上的主机,而端口号用于标识主机上的特定应用程序。在 Linux 下,使用 struct sockaddr_in
结构体来表示 IPv4 地址,其定义如下:
“`c
struct sockaddr_in {
sa_family_t sin_family; / 地址族,AF_INET 表示 IPv4 /
in_port_t sin_port; / 端口号,需要转换为网络字节序 /
struct in_addr sin_addr; / IP 地址 /
};
struct in_addr {
in_addr_t s_addr; / IP 地址,需要转换为网络字节序 /
};
“`
1.2 网络字节序
由于不同主机可能使用不同的字节序(大端序或小端序),因此在网络通信中需要使用统一的字节序,即网络字节序(大端序)。我们需要使用 htons()
、htonl()
函数将主机字节序转换为网络字节序,使用 ntohs()
、ntohl()
函数将网络字节序转换为主机字节序。
2. UDP 编程 API 函数
Linux 下 UDP 编程主要使用以下 API 函数:
socket()
: 创建套接字。bind()
: 绑定套接字到本地地址和端口。sendto()
: 发送数据。recvfrom()
: 接收数据。close()
: 关闭套接字。
2.1 socket()
c
int socket(int domain, int type, int protocol);
domain
: 地址族,AF_INET 表示 IPv4。type
: 套接字类型,SOCK_DGRAM 表示 UDP。protocol
: 协议,通常为 0。
2.2 bind()
c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd
: 套接字描述符。addr
: 指向 sockaddr 结构体的指针,包含要绑定的地址和端口。addrlen
: sockaddr 结构体的长度。
2.3 sendto()
c
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd
: 套接字描述符。buf
: 要发送的数据缓冲区。len
: 要发送的数据长度。flags
: 通常为 0。dest_addr
: 指向 sockaddr 结构体的指针,包含目标地址和端口。addrlen
: sockaddr 结构体的长度。
2.4 recvfrom()
c
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
sockfd
: 套接字描述符。buf
: 接收数据缓冲区。len
: 缓冲区大小。flags
: 通常为 0。src_addr
: 指向 sockaddr 结构体的指针,用于存储发送方的地址和端口。addrlen
: sockaddr 结构体的长度指针。
3. UDP 编程示例
以下是一个简单的 UDP 服务器和客户端示例程序:
3.1 UDP 服务器 (server.c)
“`c
include
include
include
include
include
include
include
define SERVER_PORT 8888
int main() {
int sockfd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
char buffer[1024];
// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(1);
}
// 绑定地址和端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有接口
server_addr.sin_port = htons(SERVER_PORT);
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
exit(1);
}
printf("UDP server listening on port %d...\n", SERVER_PORT);
while (1) {
client_addr_len = sizeof(client_addr);
int n = recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr *)&client_addr, &client_addr_len);
if (n < 0) {
perror("recvfrom");
exit(1);
}
printf("Received from %s:%d: %s\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer);
// 发送回复
sendto(sockfd, buffer, n, 0, (struct sockaddr *)&client_addr, client_addr_len);
}
close(sockfd);
return 0;
}
“`
3.2 UDP 客户端 (client.c)
“`c
include
include
include
include
include
include
include
define SERVER_IP “127.0.0.1”
define SERVER_PORT 8888
int main() {
int sockfd;
struct sockaddr_in server_addr;
char buffer[1024];
// 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(1);
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {
perror("inet_pton");
exit(1);
}
while (1) {
printf("Enter message: ");
fgets(buffer, sizeof(buffer), stdin);
// 发送数据
sendto(sockfd, buffer, strlen(buffer), 0,
(struct sockaddr *)&server_addr, sizeof(server_addr));
// 接收回复
int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
if (n < 0) {
perror("recvfrom");
exit(1);
}
printf("Server replied: %s", buffer);
}
close(sockfd);
return 0;
}
“`
4. 编译和运行
使用以下命令编译服务器和客户端程序:
bash
gcc server.c -o server
gcc client.c -o client
首先运行服务器程序:
bash
./server
然后在另一个终端运行客户端程序:
bash
./client
现在,客户端可以向服务器发送消息,服务器将接收消息并将其回显给客户端。
5. 总结
本文介绍了 Linux 下 UDP 编程的基础知识和 API 函数,并提供了一个简单的服务器和客户端示例程序。UDP 编程相对简单,但需要注意字节序转换和错误处理。 通过理解本文的内容,读者可以开始编写自己的 UDP 应用程序。 希望本文能帮助读者快速入门 Linux 下 UDP 编程。 更深入的学习可以参考 Linux 系统编程相关的书籍和文档,以及探索更高级的主题,例如多播和广播。