一、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→二进制 IP IP 字符串,地址指针 客户端配置服务器 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 最标准、最原生的写法
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐