关于UDP客户端给服务器发消息,服务器端显示收到消息的代码详细注释(linux)
一、服务器端代码
/* UDP 服务器实现 */
#include // 标准输入输出(如printf)
#include // 标准库函数(如exit)
#include // 字符串操作(如memset)
#include // POSIX系统调用(如close)
#include // 套接字相关函数(如socket, bind)
#include // 系统数据类型(如socklen_t)
#include // 文件控制(如open,但本代码未使用)
#include // 文件状态(未实际使用)
#include // 网络地址结构(如sockaddr_in)
#include // IP地址转换(如inet_addr)
#define IP "192.168.53.128" // 服务器监听的IP地址
#define PORT 5555 // 服务器监听的端口号
int main(int argc,char *argv[]) {
// (已注释掉)原命令行参数检查,现使用宏定义IP/PORT
// if(argc!=3){
// printf("Usage:%s ip port
",argv[0]);
// exit(0);
// }
// 1. 创建UDP套接字
// AF_INET: IPv4协议族
// SOCK_DGRAM: UDP协议
// 0: 自动选择协议(UDP固定为0)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd == -1){ // 创建失败处理
perror("socket"); // 打印错误信息(如权限不足)
exit(-1); // 异常退出
}
// 2. 绑定IP和端口号
struct sockaddr_in addr; // IPv4地址结构体
addr.sin_family = AF_INET; // 协议族:IPv4
addr.sin_port = htons(PORT); // 端口号转网络字节序
addr.sin_addr.s_addr = inet_addr(IP); // IP地址转网络格式
// 绑定套接字到指定IP和端口
int res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if(res == -1){ // 绑定失败处理
perror("bind"); // 打印错误(如端口被占用)
exit(-1);
}
// 3. 通信循环
char buf[128] = {}; // 接收缓冲区(初始化为0)
struct sockaddr_in send_addr; // 存储客户端地址信息
socklen_t len = sizeof(send_addr); // 地址结构体长度
while(1){ // 无限循环处理客户端消息
// 接收数据:
// sockfd: 套接字描述符
// buf: 接收缓冲区
// sizeof(buf): 缓冲区大小
// 0: 默认标志位
// send_addr: 发送方地址(输出参数)
// len: 地址结构体长度(输入输出参数)
res = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&send_addr, &len);
if(res == -1){ // 接收失败处理
perror("recvfrom");
exit(-1);
}
// 打印客户端信息:
// inet_ntoa: 将IP转为点分十进制字符串
// ntohs: 端口号转主机字节序
printf("%s %hu说:%s
",
inet_ntoa(send_addr.sin_addr), // 客户端IP
ntohs(send_addr.sin_port), // 客户端端口
buf); // 接收的消息
memset(buf, 0, sizeof(buf)); // 清空缓冲区
}
close(sockfd); // 关闭套接字(实际不会执行到此处)
return 0; // 程序结束
}
这段代码实现了一个 UDP 服务器 的基本功能,主要作用是接收客户端发送的消息并显示消息来源信息。以下是它的核心功能分解:
1. 核心功能
| 功能 | 说明 |
|---|---|
| 创建 UDP 套接字 | 使用 socket(AF_INET, SOCK_DGRAM, 0) 创建一个 UDP 协议的通信端点。 |
| 绑定 IP 和端口 | 将套接字绑定到固定 IP (192.168.53.128) 和端口 (5555),等待客户端连接。 |
| 接收客户端消息 | 通过 recvfrom() 持续接收客户端发送的数据,并打印客户端 IP、端口和消息内容。 |
| 实时显示消息 | 每收到一条消息,立即在终端打印格式化的信息(如 192.168.1.100 12345说: Hello)。 |
2. 技术细节
-
协议类型:UDP(无连接、不可靠但高效,适合局域网通信)。
-
硬编码配置:IP 和端口通过宏定义(
#define)固定,需手动修改代码调整。 -
数据接收:
-
使用
recvfrom()获取数据 及客户端地址(无需提前建立连接)。 -
缓冲区大小固定为 128 字节,超出部分会被截断。
-
-
错误处理:对关键操作(如
socket、bind、recvfrom)进行错误检查,失败时打印错误信息并退出。
3. 典型使用场景
-
局域网聊天室
-
多个客户端向该服务器发送消息,服务器集中显示所有消息来源和内容。
-
-
传感器数据收集
-
IoT 设备(如温度传感器)通过 UDP 上报数据,服务器记录并显示。
-
-
简单网络调试
-
测试网络连通性或端口是否开放。
-
4. 代码局限性
| 问题 | 可能的影响 | 改进建议 |
|---|---|---|
| 硬编码 IP/端口 | 每次修改需重新编译代码 | 改用命令行参数传递(如 ./server IP PORT) |
| 无数据回显功能 | 服务器不能回复客户端 | 添加 sendto() 向客户端发送响应 |
| 单线程阻塞接收 | 无法同时处理多个客户端的高并发请求 | 使用多线程或 select() 模型 |
| 缓冲区固定大小 | 长消息会被截断 | 动态分配缓冲区或循环接收 |
5. 扩展功能建议
-
日志记录:将收到的消息写入文件(如
syslog)。 -
消息广播:将某个客户端的消息转发给其他所有客户端。
-
简单协议:定义消息格式(如
JSON),便于解析结构化数据。
总结
这段代码是一个 基础的 UDP 服务器框架,适合学习网络编程或快速搭建简单的本地网络服务。如需实际应用,需根据需求扩展错误处理、并发支持和协议设计。
二、客户端代码
/* UDP 客户端实现 */
#include // 标准输入输出(如printf、fgets)
#include // 标准库函数(如exit)
#include // 字符串操作(如strlen、strtok)
#include // POSIX系统调用(如close)
#include // 套接字相关函数(如socket、sendto)
#include // 系统数据类型(如socklen_t)
#include // 文件控制(本代码未使用)
#include // 文件状态(本代码未使用)
#include // 网络地址结构(如sockaddr_in)
#include // IP地址转换(如inet_addr)
#define IP "192.168.53.128" // 目标服务器的IP地址(需修改为实际地址)
#define PORT 5555 // 目标服务器的端口号(需修改为实际端口)
int main(int argc, char *argv[]) {
// (已注释掉)原命令行参数检查,现使用宏定义IP/PORT
// if(argc!=3){
// printf("Usage:%s ip port
",argv[0]);
// exit(0);
// }
// 1. 创建UDP套接字
// AF_INET: IPv4协议
// SOCK_DGRAM: UDP协议
// 0: 自动选择协议(UDP固定为0)
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) { // 创建失败处理
perror("socket"); // 打印错误(如权限不足)
exit(-1);
}
// 2. 配置目标服务器地址
struct sockaddr_in addr; // IPv4地址结构体
addr.sin_family = AF_INET; // 协议族:IPv4
addr.sin_port = htons(PORT); // 端口号转网络字节序
addr.sin_addr.s_addr = inet_addr(IP); // IP地址转网络格式
// 3. 通信循环
char buf[128] = {}; // 发送缓冲区(初始化为0)
while (1) {
printf("请输入:"); // 提示用户输入
fgets(buf, sizeof(buf), stdin); // 从标准输入读取一行(含换行符)
// 移除输入末尾的换行符(
)
strtok(buf, "
");
// 发送数据到服务器:
// sockfd: 套接字描述符
// buf: 发送的数据
// strlen(buf)+1: 数据长度(包含字符串结束符 )
// 0: 默认标志位
// (struct sockaddr*)&addr: 目标服务器地址
// sizeof(addr): 地址结构体长度
sendto(sockfd, buf, strlen(buf) + 1, 0,
(struct sockaddr *)&addr, sizeof(addr));
}
close(sockfd); // 关闭套接字(实际不会执行到此处)
return 0; // 程序结束
}
UDP 客户端功能概述
-
核心功能:向指定的 UDP 服务器 (
IP:PORT) 发送用户输入的消息。 -
通信方式:无连接(UDP),直接通过
sendto()发送数据。 -
特点:
-
持续等待用户输入(
while(1)循环)。 -
硬编码服务器地址(需提前修改代码中的
IP和PORT)。
-
关键功能说明
| 代码部分 | 功能说明 |
|---|---|
socket(AF_INET, SOCK_DGRAM, 0) | 创建 UDP 套接字(无连接,不可靠但高效)。 |
addr.sin_port = htons(PORT) | 将端口号转换为 网络字节序(大端模式),确保跨平台兼容性。 |
inet_addr(IP) | 将点分十进制IP(如 "192.168.1.100")转换为32位网络字节序整数。 |
fgets(buf, sizeof(buf), stdin) | 从用户输入读取一行文本(包含换行符
),存入缓冲区 buf。 |
strtok(buf, "
") | 移除缓冲区末尾的换行符(避免发送多余字符)。 |
sendto(...) | 向目标服务器发送数据,无需提前建立连接(UDP特性)。 |
使用示例
-
运行客户端:
bash
./udp_client
-
输入消息:
bash
请输入: Hello Server!
-
服务器端:会显示接收到的消息和客户端地址。
注意事项
-
硬编码问题:
-
需手动修改代码中的
IP和PORT以匹配服务器地址。 -
改进建议:改用命令行参数(如
./client)。
-
-
数据完整性:
-
UDP 不保证消息到达顺序或是否丢失,适合对可靠性要求不高的场景。
-
-
缓冲区限制:
-
输入超过 128 字节会被截断,需动态扩展缓冲区或循环发送。
-
-
无错误处理:
-
sendto()的返回值未检查,实际应用中需处理网络错误(如目标不可达)。
-
扩展建议
-
添加回复功能:在
sendto后调用recvfrom接收服务器响应。 -
多线程输入:分离用户输入和网络接收逻辑,避免阻塞。
-
协议设计:定义消息头(如长度、类型)以支持复杂数据交换。
三、终端效果:









