当前位置: 首页 > news >正文

Linux UDP 网络编程

一、UDP 基础概念

UDP(用户数据报协议)是无连接、不可靠、面向数据报的传输协议,有两种编程写法:

  1. 连接模式:客户端connect绑定服务器 →send发送
  2. 无连接模式:客户端不connectsendto直接指定目标发送服务器代码两种模式完全通用

二、UDP 编程必备头文件

#include <sys/types.h> // 系统基础数据类型 #include <sys/socket.h> // 套接字核心函数 #include <netinet/in.h> // IPv4地址结构体 sockaddr_in #include <arpa/inet.h> // IP地址转换函数 #include <unistd.h> // 关闭套接字 #include <stdio.h> // 输入输出 #include <string.h> // 内存操作

三、核心函数详解(参数表格,新手必背)

1. socket () —— 创建套接字

int socket(int domain, int type, int protocol);
参数名含义UDP 固定取值作用
domain协议族AF_INET使用 IPv4 网络协议
type套接字类型SOCK_DGRAM指定为UDP 数据报套接字
protocol具体协议0系统自动匹配 UDP 协议
返回值执行结果成功:文件描述符失败:-1用于后续通信操作

2. bind () —— 服务器绑定端口

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

表格

参数名含义取值作用
sockfd套接字socket () 返回值服务器监听套接字
addr地址结构体服务器 IP + 端口绑定本机地址与端口
addrlen结构体长度sizeof(struct sockaddr_in)地址长度
返回值执行结果成功:0失败:-1服务器必须绑定成功

3. sendto () —— UDP 无连接发送

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数名含义取值作用
sockfd客户端套接字socket () 返回值通信通道
buf发送缓冲区自定义数组存储要发送的数据
len数据长度strlen(缓冲区)实际发送字节数
flags标志位0默认无特殊操作
dest_addr目标地址服务器 IP + 端口指定接收方
addrlen地址长度结构体大小地址参数长度
返回值发送长度成功:发送字节数失败:-1-

4. recvfrom () —— UDP 接收数据

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数名含义取值作用
sockfd服务器套接字socket () 返回值监听通道
buf接收缓冲区自定义数组存储接收数据
len最大长度缓冲区大小 - 1防止溢出
flags标志位0默认操作
src_addr发送方地址客户端 IP + 端口自动存储客户端信息
addrlen地址长度指针变量地址传入传出参数
返回值接收长度成功:接收字节数失败:-1-

5. 辅助函数

函数功能参数作用
htons()端口转网络字节序端口号网络传输必须转换
inet_aton()字符串 IP→二进制 IPIP 字符串,地址指针客户端配置服务器 IP
inet_ntoa()二进制 IP→字符串 IP网络 IP服务器打印客户端 IP

四、UDP 双模式通信流程图(明文版)

模式 1:UDP 连接模式(connect + send)

┌───────────────────────────┐ ┌───────────────────────────┐ │ UDP 客户端(connect) │ │ UDP 服务器 │ └────────────┬──────────────┘ └────────────┬──────────────┘ │ │ │ 1. 创建 socket() │ 1. 创建 socket() │ │ │ 2. 配置服务器IP+端口 │ 2. 配置本地IP+端口 │ │ │ 3. connect() 绑定服务器 │ 3. bind() 绑定端口 │ │ │ 4. 键盘输入数据 │ 4. 循环阻塞 recvfrom() │ │ │ 5. send() 发送数据 ┌──────▼───────┐ └───────────────────────────────► 接收数据+打印客户端IP │ └──────────────┘

模式 2:UDP 无连接模式(sendto)

┌───────────────────────────┐ ┌───────────────────────────┐ │ UDP 客户端(无connect) │ │ UDP 服务器 │ └────────────┬──────────────┘ └────────────┬──────────────┘ │ │ │ 1. 创建 socket() │ 1. 创建 socket() │ │ │ 2. 配置服务器IP+端口 │ 2. bind() 绑定端口 │ │ │ 3. 不执行 connect(原生无连接) │ 3. 循环阻塞 recvfrom() │ │ │ 4. 键盘输入数据 │ 等待客户端数据 │ │ │ 5. sendto() 指定目标发送数据 ┌──────▼───────┐ └───────────────────────────────────► 接收数据+打印客户端IP │ └──────────────┘

五、完整带注释代码

5.1 UDP 客户端(无连接模式 sendto)

#include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> // 服务器端口,必须与服务器一致 #define SERVER_PORT 8888 int main(int argc, char **argv) { int iSocketClient; // 客户端套接字 struct sockaddr_in tSocketServerAddr; // 服务器地址结构体 int iSendLen; // 发送数据长度 unsigned char ucSendBuf[1000]; // 发送缓冲区 int iAddrLen; // 地址长度 // 校验命令行参数:必须传入服务器IP if (argc != 2) { printf("Usage:\n"); printf("%s <server_ip>\n", argv[0]); return -1; } // 1. 创建UDP套接字 iSocketClient = socket(AF_INET, SOCK_DGRAM, 0); // 2. 配置服务器地址 tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); // 端口转网络字节序 // 字符串IP转换为网络IP if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)) { printf("invalid server_ip\n"); return -1; } memset(tSocketServerAddr.sin_zero, 0, 8); // 3. 循环读取键盘数据并发送 while (1) { if (fgets(ucSendBuf, 999, stdin)) { iAddrLen = sizeof(struct sockaddr); // 核心:sendto 直接指定服务器发送数据 iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0, (const struct sockaddr *)&tSocketServerAddr, iAddrLen); // 发送失败处理 if (iSendLen <= 0) { close(iSocketClient); return -1; } } } return 0; }

5.2 UDP 服务器(通用,双模式兼容)

#include <sys/types.h> #include <sys/socket.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> // 服务器监听端口 #define SERVER_PORT 8888 int main(int argc, char **argv) { int iSocketServer; // 服务器监听套接字 struct sockaddr_in tSocketServerAddr; // 服务器地址 struct sockaddr_in tSocketClientAddr; // 客户端地址 int iAddrLen; // 地址长度 int iRecvLen; // 接收数据长度 unsigned char ucRecvBuf[1000]; // 接收缓冲区 // 1. 创建UDP套接字 iSocketServer = socket(AF_INET, SOCK_DGRAM, 0); if (-1 == iSocketServer) { printf("socket error!\n"); return -1; } // 2. 配置服务器地址:监听所有网卡 tSocketServerAddr.sin_family = AF_INET; tSocketServerAddr.sin_port = htons(SERVER_PORT); tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; memset(tSocketServerAddr.sin_zero, 0, 8); // 3. 绑定IP+端口(服务器必须执行) if (-1 == bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr))) { printf("bind error!\n"); return -1; } // 4. 循环接收客户端数据 while (1) { iAddrLen = sizeof(struct sockaddr); // 接收数据并获取客户端IP/端口 iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen); if (iRecvLen > 0) { ucRecvBuf[iRecvLen] = '\0'; // 添加结束符,防止乱码 printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf); } } close(iSocketServer); return 0; }

六、编译与运行

1. 编译服务器

gcc udp_server.c -o udp_server

2. 运行服务器

./udp_server

3. 编译客户端

gcc udp_client.c -o udp_client

4. 运行客户端

./udp_client 127.0.0.1

七、UDP 双模式对比

对比项connect + send(连接模式)sendto(无连接模式)
连接状态伪连接,绑定固定服务器真正无连接
发送方式无需指定目标每次指定目标地址
灵活性低,仅发一个服务器高,可发多个服务器
UDP 本质封装简化写法原生标准写法
适用场景固定单点通信多目标 / 广播通信

八、新手常见问题

  1. bind 报错:端口被占用,更换端口即可
  2. 收不到数据:IP / 端口填写错误
  3. 乱码:接收数据未加\0结束符
  4. 发送失败:地址结构体配置错误

九、核心总结

  1. 服务器固定流程socket → bind → recvfrom
  2. 无连接客户端流程socket → 配置地址 → sendto
  3. 连接客户端流程socket → connect → send
  4. recvfrom自动获取客户端信息,是 UDP 服务器核心
  5. 无连接模式是 UDP 最标准、最原生的写法
http://www.jsqmd.com/news/570049/

相关文章:

  • Endnote与WPS高效协作:自动与手动关联全攻略
  • 2026年口碑好的夜景亮化工程/文旅景观亮化工程推荐施工方案 - 品牌宣传支持者
  • 重新定义宝可梦体验:Universal Pokemon Randomizer ZX 全面解析与使用指南
  • C++ AVL树
  • 为“自感”留白
  • 突破百度网盘限速:BaiduPCS-Go命令行工具深度解析
  • 2026年质量好的台历书刊印刷/广告书刊印刷/折页书刊印刷/成都书刊印刷厂家推荐哪家好 - 品牌宣传支持者
  • 上海腕表售后大数据揭秘:从百达翡丽到浪琴,高端腕表故障图谱与北京名表价格的隐性关联——京沪杭宁深锡六城12,000次维修案例深度解析 - 时光修表匠
  • Pixel Couplet Gen快速上手:MIT开源镜像免配置部署微信小程序前端
  • GitHub加速插件技术解析:300%速度提升的实现原理与实践指南
  • 为什么选择Zabbix而不是Prometheus?K8s监控工具深度对比与实战配置
  • 腾讯开源翻译大模型HY-MT1.5-7B镜像使用教程:新手快速入门
  • Real-ESRGAN-GUI:让模糊图像重获新生的AI超分辨率神器
  • 苹果50周年:辉煌背后的创新困境与未来挑战
  • 上海腕表售后全解析:从北京名表价格看高端腕表养护与维修逻辑 - 时光修表匠
  • 在ESP32上为LVGL 8.x添加中文输入法:从拼音到候选词显示的完整实现
  • Snap Hutao:原神玩家的全方位数据管理解决方案
  • 2026年知名的浓缩设备/食品级血浆蛋白浓缩设备/酶制剂浓缩设备/乳品蛋白浓缩设备厂家推荐哪家好 - 品牌宣传支持者
  • 2269 上市公司智慧供应链对数字创新的平均处理效应指标【ATT】(2000-2024)
  • 京东茅台自动抢购实战指南:高效自动化解决方案
  • Qwen3.5-2B开源大模型部署教程:支持商用、可审计、易集成的端侧AI方案
  • 2026Altium Designer 国产替代软件推荐,如何选到靠谱的国产 EDA? - 品牌2026
  • 【完整源码+数据集+部署教程】对话框按钮检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]
  • Ollama平台ChatGLM3-6B-128K应用:支持工具调用的Agent系统搭建
  • Ubuntu 22.04 LTS下Samba共享配置全攻略:从安装到多设备访问
  • 告别Keil5刺眼白屏!保姆级教程教你配置VS Code同款暗黑主题(附3套配色方案)
  • 别只盯着喂食!用STM32打造宠物环境管家:温湿度、光照、水位全自动调节
  • 用74LS194和555定时器DIY流水灯:一个经典的数字电路课程设计复盘(附Multisim仿真文件)
  • 别再死记硬背了!用Arduino和ESP32手把手演示I2C的‘线与’与上拉电阻到底怎么用
  • 破解电竞内容创作效率瓶颈:League Director如何通过多维度控制实现视频制作革命