Linux平台下UDP客户/服务端程序
图片来源:https://subingwen.cn/linux/udp/
UDP服务器无法直接检测客户端断开连接。
UDP 服务端
server.cpp
cpp
#include <iostream>
#include <cstdlib> // std::exit
#include <cstring> // memset
#include <arpa/inet.h> // socket, bind, recvfrom, sendto,SOCK_DGRAM
#include <unistd.h> // close
int main()
{
// 1. 创建 UDP 套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个UDP套接字
if (sockfd == -1)
{
perror("socket"); // 错误处理
std::exit(EXIT_FAILURE);
}
// 2. 将socket()返回值和本地的IP端口绑定到一起
sockaddr_in addr; // 用于存储地址信息
addr.sin_family = AF_INET; // 地址族,IPv4
addr.sin_port = htons(10000); // 转换服务器的端口号10000为网络字节序
addr.sin_addr.s_addr = INADDR_ANY; // 绑定到任意IP地址
// inet_pton(AF_INET, "172.31.108.107", &addr.sin_addr.s_addr); // 指定IP地址
int ret = bind(sockfd, (sockaddr*)&addr, sizeof(addr)); // 绑定套接字到地址
if (ret == -1)
{
perror("bind"); // 错误处理
std::exit(EXIT_FAILURE);
}
// 3. 和客户端通信
while (true)
{
char buf[1024]; // 接收缓冲区
memset(buf, 0, sizeof(buf)); // 清零缓冲区
sockaddr_in cliaddr; // 用于存储客户端地址信息
socklen_t clilen = sizeof(cliaddr); // 客户端地址结构的大小
// 接收数据
int len = recvfrom(sockfd, buf, sizeof(buf), 0, (sockaddr*)&cliaddr, &clilen); // 从客户端读取数据
if (len > 0)
{
std::cout << "客户端: " << buf << std::endl; // 打印客户端发送的消息
// 回复客户端
std::string response = "你好, 客户端...\n";
sendto(sockfd, response.c_str(), response.size(), 0, (sockaddr*)&cliaddr, clilen); // 发送响应
}
else
{
perror("recvfrom"); // 错误处理
break;
}
}
close(sockfd); // 关闭UDP套接字
return 0;
}
编译与运行
bash
g++ server.cpp -o server
./server
UDP 客户端
client.cpp
cpp
#include <iostream>
#include <cstdlib> // std::exit
#include <cstring> // memset, strlen
#include <arpa/inet.h> // socket, sendto, recvfrom, inet_pton, htons
#include <unistd.h> // close, sleep
int main()
{
// 1. 创建通信的套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建一个UDP套接字
if (sockfd == -1)
{
perror("socket"); // 错误处理
std::exit(EXIT_FAILURE);
}
// 2. 准备服务器地址信息
sockaddr_in addr; // 用于存储服务器地址信息
addr.sin_family = AF_INET; // 地址族,IPv4
addr.sin_port = htons(10000); // 大端端口转换
inet_pton(AF_INET, "172.31.108.107", &addr.sin_addr.s_addr); // 将IP地址转换为网络字节顺序
// 3. 和服务器端通信
int number = 0;
while (true)
{
// 发送数据
char buf[1024]; // 数据缓冲区
sprintf(buf, "你好, 服务器...\n", number++); // 格式化字符串
sendto(sockfd, buf, strlen(buf), 0, (sockaddr*)&addr, sizeof(addr)); // 发送数据
// 接收数据
memset(buf, 0, sizeof(buf)); // 清空缓冲区
sockaddr_in recv_addr; // 用于存储服务器地址信息
socklen_t addr_len = sizeof(recv_addr);
int len = recvfrom(sockfd, buf, sizeof(buf), 0, (sockaddr*)&recv_addr, &addr_len); // 从服务器读取数据
if (len > 0)
{
std::cout << "服务器: " << buf; // 打印服务器发送的消息
}
else
{
perror("recvfrom");
break;
}
sleep(1); // 每隔1秒发送一条数据
}
close(sockfd); // 关闭套接字
return 0;
}
编译与运行
bash
g++ client.cpp -o client
./client
相关头文件介绍
使用到的函数和宏除了<sys/socket.h>
的 recvfrom
、sendto
和 SOCK_DGRAM
,其他使用到的函数和宏与TCP网络通信中的基本一致。
<sys/socket.h>
:
-
bytes_sent = recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
:用于接收数据报文,适用于无连接的套接字(如 UDP)。它允许从任何源地址接收数据。成功时返回接收到的字节数,失败时返回-1
。 参数:sockfd
:接收数据的套接字描述符。buf
:指向接收数据的缓冲区。len
:缓冲区的长度,表示最大接收字节数。flags
:接收选项的标志,通常为0
。src_addr
:指向sockaddr
结构体的指针,用于存储发送方的地址信息。addrlen
:指向socklen_t
类型的指针,表示src_addr
结构体的长度。
-
bytes_sent = sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
:用于发送数据报文,适用于无连接的套接字(如 UDP)。它允许将数据发送到指定的目标地址。成功时返回实际发送的字节数,失败时返回-1
。 参数:sockfd
:发送数据的套接字描述符。buf
:指向要发送数据的缓冲区。len
:要发送的字节数。flags
:发送选项的标志,通常为0
。dest_addr
:指向目标地址的sockaddr
结构体。addrlen
:目标地址的长度。
-
SOCK_DGRAM
:用于创建数据报套接字,表示使用无连接的 UDP 协议进行数据传输。