
UDP
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
UDP
1. UDP 概述
UDP 网络通信协议特色
- 面向无连接
- 数据传递速度较快
- 数据传递不安全,无法保证数据传递的完整性。
网络通信和本地主机在内存【字节序】不同,主机采用的都是【小端字节序】,网络数据传递过程中采用的数据字节序为【大端字节序】

利用相关函数将本地的小端字节序数据,转换网络通信使用的大端字节序数据内容。
#include <stdio.h>
// 当前 Data 是共用体,目前情况下,占用内存字节数为 4 个字节
union Data
{
int val;
char arr[4];
};
int main(int argc, char const *argv[])
{
union Data data;
// 当前给予共用体 Data 中 val 赋值数据为 0x12345678
// 0x12345678 为十六进制数据,占用内存 4 个字节。
data.val = 0x12345678;
// 小端字节序数据存储方式
printf("data.arr[0] = 0x%02x\n", data.arr[0]); // 0x78
printf("data.arr[1] = 0x%02x\n", data.arr[1]); // 0x56
printf("data.arr[2] = 0x%02x\n", data.arr[2]); // 0x34
printf("data.arr[3] = 0x%02x\n", data.arr[3]); // 0x12
return 0;
}
2. 本机数据和网络数据转换相关函数
2.1.1 htonl 函数
函数文档
#include <arpa/inet.h> uint32_t htonl(uint32_t host_int_32);
- 函数功能
- 将 32 位主机字节序转换为网络字节序
Host to Network Long
- 形式参数列表
uint32_t host_int_32 :
无符号 int 类型数据unsigned int
- 返回值类型
- 返回值类型
uint32_t
,得到当前网络字节序数据内容。
2.1.2 htons 函数
函数文档
#include <arpa/inet.h> uint16_t htons(uint16_t host_int_16);
- 函数功能
- 将 16 位主机字节序转换为网络字节序
Host to Network short
- 形式参数列表
uint16_t host_int_16 :
要求提供的数据是一个 16 位无符号整数类型,对应unsigned short
- 返回值类型
- 返回值类型
uint16_t
,得到当前网络字节序数据内容。
2.1.3 ntohl 函数
函数文档
#include <arpa/inet.h> uint32_t ntohl(uint32_t net_int_32);
- 函数功能
- 将 32 位网络字节序转换为本机字节序
Network to Host Long
- 形式参数列表
uint32_t net_int_32:
无符号 int 类型数据unsigned int
网络字节序数据- 返回值类型
- 返回值类型
uint32_t
,得到当前本机字节序数据内容。
2.1.4 ntohs 函数
函数文档
#include <arpa/inet.h> uint16_t ntohs(uint16_t net_int_16);
- 函数功能
- 将 16 位网络字节序转换为本机字节序
Network to Host short
- 形式参数列表
uint16_t net_int_16:
要求提供的数据是一个 16 位无符号整数类型,对应unsigned short
- 返回值类型
- 返回值类型
uint16_t
,得到当前本机字节序数据内容。
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
// 32 位数据
uint32_t data_32 = 0x12345678;
uint32_t net_data_32 = 0;
// 16 位数据
uint16_t data_16 = 0xABCD;
uint16_t net_data_16 = 0;
/*
uint32_t htonl(uint32_t host_int_32);
uint32_t ntohl(uint32_t net_int_32);
*/
net_data_32 = htonl(data_32);
printf("data_32 : %#x\n", data_32);
printf("net_data_32 : %#x\n", net_data_32);
printf("ntohl(net_data_32) : %#x\n", ntohl(net_data_32));
printf("-----------------------------\n");
/*
uint16_t htons(uint16_t host_int_16);
uint16_t ntohs(uint16_t net_int_16);
*/
net_data_16 = htons(data_16);
printf("data_16 : %#x\n", data_16);
printf("net_data_16 : %#x\n", net_data_16);
printf("ntohs(net_data_16) : %#x\n", ntohs(net_data_16));
return 0;
}
3. IP 地址转换函数
3.1 inet_pton
函数文档
#include <arpa/inet.h> int inet_pton(int family, const char *src, void *addrptr);
- 函数功能:
- 将【点分十进制】字符串,例如
"192.168.13.20"
转换为 32位整数 IP 描述形式- 函数参数
int family :
协议族,当前函数支持 IPv4 和 IPv6,协议族有AF_INET
,AF_INET6
const char *src :
提供给当前函数【点分十进制】字符串,例如 `"192.168.13.20"void *addrptr :
目前十进制存储当前 IP 地址数据的变量地址- 返回值
- 若转换成功,返回 1。
- 若输入的地址族不支持,返回 0。
- 若输入的字符串不是有效的 IP 地址格式,返回 -1,并设置
errno
为EINVAL
。
3.2 inet_ntop
函数文档
#include <arpa/inet.h> const char *inet_ntop(int family, const void *addrstr, char *str_ip_addr, socklen_t size);
- 函数功能:
- 将 32 位整数 IP 地址,按照协议族要求,转换为目标 IP 【点分十进制】地址,支持 IPv4 和 IPv6
- 函数参数
int family :
协议族,当前函数支持 IPv4 和 IPv6,协议族有AF_INET
,AF_INET6
const void *addrstr :
32位整数 IP 变量首地址char *str_ip_addr :
存储 IP 地址的 char 类型缓冲空间, 最小要求 16 个字节socklen_t size :
unsigned int
对应 str_ip_addr 缓冲区字节长度- 返回值
- 若转换成功,返回对应IP 【点分十进制】地址
- 失败返回 NULL
tips:
- "192.168.13.20" ==> int 32 位数据 ==> "192.168.13.20"
- 字符数组要求最小内存空间需求多少? 16 = 3(每一 IP 位 3 个字符) * 4(位) + 3(点分隔符) + 1(\0)
#include <stdio.h>
#include <arpa/inet.h>
#define ADDR_BUFFER_SIZE 16
int main(int argc, char const *argv[])
{
// 点分十进制 IP 字符串地址
char *ip_addr = "192.168.13.20";
// 用于存储网络端所需的 32位 整数 IP 地址数据
int net_addr = 0;
inet_pton(
AF_INET, // IPv4 协议
ip_addr, // 点分十进制 IP 字符串地址
&net_addr // 存储网络所需 32位 整数 IP 地址变量地址
);
printf("ip_addr : %s\n", ip_addr);
printf("net_addr : %d\n", net_addr);
/*
ip_addr ==> "192.168.13.20"
对应的二进制位数据内容为
1100 0000
1010 1000
0000 1101
0001 0100
net_addr ==> 336439488 32位 十进制
对应的二进制位数据内容为
0001 0100
0000 1101
1010 1000
1100 0000
将本机 IP 点分十进制地址【小端字节序】,转换为 32 位 int 类型网络所需的
大端字节序数据.
*/
char addr_buffer[ADDR_BUFFER_SIZE] = "";
inet_ntop(
AF_INET, // 协议要求为 IPv4 协议
&net_addr, // 网络 32位 int IP 地址变量
addr_buffer, // 存储 点分十进制 IP 字符串缓冲区
ADDR_BUFFER_SIZE // 缓冲区大写
);
printf("addr_buffer : %s\n", addr_buffer);
printf("inet_ntop : %s\n", inet_ntop(
AF_INET, // 协议要求为 IPv4 协议
&net_addr, // 网络 32位 int IP 地址变量
addr_buffer, // 存储 点分十进制 IP 字符串缓冲区
ADDR_BUFFER_SIZE // 缓冲区大小
));
printf("addr_buffer : %p\n", addr_buffer);
printf("inet_ntop : %p\n", inet_ntop(
AF_INET, // 协议要求为 IPv4 协议
&net_addr, // 网络 32位 int IP 地址变量
addr_buffer, // 存储 点分十进制 IP 字符串缓冲区
ADDR_BUFFER_SIZE // 缓冲区大小
));
return 0;
}
4. socket
4.1 概述
socket 是一个网络通信数据【管道】,要求通信的主机必须支持 socket 协议,才可以通过 socket 完成两个或者多个主机之间的数据通信。
socket 中文称之为 套接字。socket 需要占用进程的【文件描述符 fd】,同时也支持使用系统调用(System Calls)的 write read close 相关函数。

4.2 socket 创建函数
函数文档
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int family, int type, int protocol);
- 函数功能:
- 创建一个 socket 网络通信管道,得到对应的 socket 文件描述符(fd)
- 函数参数:
int family
: 协议族,包括 IPv4AF_INET
, IPv6AF_INET6
int type
: socket 类型,
- 满足 TCP 要求的 socket
SOCK_STREAM
- 满足 UDP 要求 的 socket
SOCK_DGRAM
- 原始 socket 套接字
SOCK_RAW
int protocol
- 协议类型 0,自动选择默认协议
- IPPROTO_TCP 要求是 TCP 协议
- IPPROTO_UDP 要求为 UDP 协议
- 返回类型
- 创建成功,得到当前 Socket 对应的文件描述符
- 创建失败返回 -1
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
// int socket(int family, int type, int protocol);
int main(int argc, char const *argv[])
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("Socket Failed!");
}
else
{
printf("scoket fd : %d\n", fd);
}
close(fd);
return 0;
}
4.3 sendto 函数
函数文档
#include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- 函数功能:
- 将用户提供的 len 长度的 buf 数据,利用当 sockfd 对应的 socket 网络通信管道,发送到目标 sockaddr 结构体对应的主机地址,同时当前函数完成 UDP 协议要求的数据打包。
- 函数参数:
sockfd
:表示套接字描述符,是通过socket
函数创建的套接字的标识符。它指定了要使用哪个套接字来发送数据。buf
:指向要发送的数据的缓冲区的指针。可以是任意类型的数据,如字符串、结构体等。len
:指定要发送的数据的长度(以字节为单位)。flags
:用于指定发送操作的一些标志。通常可以将其设置为 0,表示使用默认行为。dest_addr
:指向目标地址的struct sockaddr
结构体指针。对于 IPv4,通常使用struct sockaddr_in
;对于 IPv6,使用struct sockaddr_in6
。该结构体包含了目标主机的 IP 地址和端口号等信息。addrlen
:表示dest_addr
结构体的长度,以字节为单位。可以使用sizeof
运算符来获取该结构体的大小。- 返回类型
- ssize_t 有符号 int 类型数据,发送成功,返回发送数据的字节数
- 失败返回 -1
补充说明
#include <sys/socket.h> struct sockaddr { sa_family_t sa_family; /* 地址族,如 AF_INET(IPv4)、AF_INET6(IPv6)等 */ char sa_data[14]; /* 具体的地址数据 */ };
#include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; /* 地址族,必须为 AF_INET */ in_port_t sin_port; /* 端口号,网络字节序 */ struct in_addr sin_addr; /* IPv4 地址 */ char sin_zero[8]; /* 填充字节,使 struct sockaddr_in 和 struct sockaddr 大小相同 */ }; struct in_addr { in_addr_t s_addr; /* 32 位 IPv4 地址,网络字节序 */ };
函数中所需参数存在 const struct sockaddr *dest_addr, 用于描述当前目标 UDP 接收端的相关网络数据内容,实际上提供给当前参数的的是 sockaddr_in 结构体数据,内容包括【协议族,端口号,IP地址】,IP 地址要求是网络字节序 ,需要使用 htonl
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
int main(int argc, char const *argv[])
{
struct sockaddr_in dst_addr;
int fd = -1;
char *str = "Hello World!";
// 1. 创建 socket
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == fd)
{
perror("socket failed!");
}
// 2. 【重点】初始化 sockaddr_in 结构体数据
/*
struct sockaddr_in {
sa_family_t sin_family; 地址族,必须为 AF_INET
in_port_t sin_port; 端口号 short,网络字节序
struct in_addr sin_addr; IPv4 地址 int ,网络字节序
};
*/
dst_addr.sin_family = AF_INET; // 设置为 IPv4 IP地址协议
dst_addr.sin_port = htons(9000); // 设置为 IPv4 IP地址协议
// 【重点】将点分十进制 IP 地址数据,转换为网络字节序所需 32位 IP 地址数据
inet_pton(AF_INET, "192.168.13.20", &(dst_addr.sin_addr));
// 3 sendto 操作
int ret = sendto(fd,
str,
strlen(str) + 1,
0,
(struct sockaddr *)&dst_addr,
sizeof(dst_addr));
if (ret > 0)
{
printf("send bytes : %d\n", ret);
}
// 4. 【重点】关闭 socket 资源
close(fd);
return 0;
}
4.4 bind 函数
函数文档
#include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 函数功能:
- 当前进程根据 socket 和 网络连接对应的相关信息结构体 绑定指定的 IP 和 端口号,可以从对应的 IP 和 端口号中获取数据
- 函数参数
int sockfd :
打开 socket 对应的文件描述符const struct sockaddr *addr :
网络 IP 信息结构体数据,主要包括协议族,IP地址和端口号,数据要求网络字节序/大端字节序socklen_t addrlen :
对应网络 IP 信息结构体数据字节个数- 返回值
- 绑定成功 返回 0
- 绑定失败 返回 -1
4.5 recvfrom 函数
函数文档
#include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- 函数功能
- 从 UDP 面向无连接的 socket 中获取数据,要求必须使用
bind
函数执行之后,才可以使用recvfrom
- 函数参数:
int sockfd :
满足 UDP 条件的 socket 对应的文件描述void *buf :
接受数据的缓冲区空间size_t len :
缓冲区字节数int flags :
标志位,通常为 0struct sockaddr *src_addr :
保存发送者对应 IP 地址信息的 struct sockaddr 结构体socklen_t *addrlen :
struct sockaddr 结构体字节数- 返回值:
- 成功时:返回实际接收到的数据字节数。如果返回值为 0,表示连接正常关闭(对于 TCP 套接字)。
- 失败时:返回 -1,并设置 errno 来指示具体的错误类型。常见的错误包括:
EAGAIN
或EWOULDBLOCK
:在非阻塞模式下,没有数据可用。ECONNRESET
:连接被对方重置。EINTR
:系统调用被信号中断。
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载必须注明来自 卡卡罗特
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果