UDP的socket编程

socket接口

int socket(int domain, int type, int protocol);

参数说明​

参数 说明
domain 协议族(地址族),如 AF_INET(IPv4)、AF_INET6(IPv6)
type 套接字类型,UDP 使用 SOCK_DGRAM(数据报)
protocol 通常设为 0(自动选择),或 IPPROTO_UDP

socket() 的前两个参数 domain(地址族)和 type(套接字类型)已经分别指定了​​网络层协议​ ​(如 IPv4/IPv6)和​​传输层协议​​(如 TCP/UDP),第三个参数是历史原因被留了下来,现在一般设为0

底层原理

socket系统调用会创建struct file,struct socket,struct sock等结构体,最终返回该套接字的文件描述符

struct sock

传输层和网络层的底层实现

cpp 复制代码
struct sock {
    struct sk_buff_head  sk_receive_queue; // 传输层接收缓冲区
    struct sk_buff_head  sk_write_queue;   // 传输层发送缓冲区
    struct proto       *sk_prot;           // 纯粹的传输层协议的操作集
    union {
        struct inet_sock  inet;            // IPv4的底层结构体
        struct ipv6_sock  ipv6;            // IPv6的底层结构体
    };
    // ...(定时器、拥塞控制、状态等)
};

struct socket

对struct sock进行封装,主要是封装出了用户级的系统调用操作集

cpp 复制代码
struct socket {
    struct sock     *sk;       
    const struct proto_ops *ops; //协议相关的系统调用​​(如 bind、connect、sendmsg)
    struct file     *file;     
};

操作集辨析

1.file_operations(struct file)​

​​文件的通用操作接口​​(readwritepoll

2.proto_ops(struct socket)

实现协议相关的系统调用​ ​(如 bindconnectsendmsg

3. struct proto(struct sock)

传输层协议的底层操作集

recvfrom 函数声明​

cpp 复制代码
ssize_t recvfrom(
    int sockfd,
    void *buf,
    size_t len,
    int flags,
    struct sockaddr *src_addr,
    socklen_t *addrlen
);

参数详解​

​参数​ ​类型​ ​说明​
sockfd int 接受数据的套接字文件描述符(由 socket() 创建)。
buf void * 接收数据的缓冲区地址,用于存储接收到的数据。
len size_t 缓冲区的最大长度(字节数),防止缓冲区溢出。
flags int 控制接收行为的标志位(如 MSG_DONTWAITMSG_PEEK),通常设为 0
src_addr struct sockaddr * 接收发送方套接字地址结构体​​的缓冲区
addrlen socklen_t * 传入缓冲区的大小,返回套接字地址结构体大小。

返回值​

​返回值​ ​说明​
> 0 成功接收到的字节数。
0 ​仅对 TCP 有效​ ​,表示连接已关闭(UDP 不会返回 0)。
-1 (失败) 出错,可通过 errno 获取错误码(如 EAGAIN 表示非阻塞模式下无数据)。

sendto函数

cpp 复制代码
ssize_t sendto(
    int sockfd,                  // 套接字文件描述符
    const void *buf,            // 待发送数据的缓冲区
    size_t len,                 // 数据长度(字节数)
    int flags,                  // 发送方式控制标志(通常设为 0)
    const struct sockaddr *dest_addr,  // 目标地址结构体
    socklen_t addrlen           // 目标地址结构体长度
);

​参数解释​

​参数​ ​类型​ ​说明​
sockfd int 套接字文件描述符(由 socket() 创建)。
buf const void * 待发送数据的缓冲区地址。
len size_t 数据的长度(字节数)。
flags int 控制发送行为的标志位(如 MSG_DONTWAITMSG_MORE),通常设为 0
dest_addr const struct sockaddr * ​目标地址结构体​ ​(如 struct sockaddr_in)。
addrlen socklen_t dest_addr 结构体的实际长度(如 sizeof(struct sockaddr_in))。

返回值​

​返回值​ ​说明​
> 0 成功发送的字节数。
-1 (失败) 出错,可通过 errno 获取错误码(如 EAGAIN 表示非阻塞模式下无法立即发送)。

udp服务器逻辑

1.利用socket函数创建套接字,传参地址族和套接字类型,第三个参数是历史遗留不用管,比如socket(AF_INET, SOCK_DGRAM, 0),说明套接字网络层是ipv4,传输层是tcp,其底层就是创建struct file还有对应的struct socket和struct sock,然后返回套接字描述符

2.接下来要将套接字绑定端口,创建ipv4对应的套接字地址结构体struct sockaddr_in,然后填写地址族AF_INET,再填写端口号和ip地址,一般端口号是靠命令行参数来给出,IP地址则是INADDR_ANY,该套接字绑定的ip是任意的,也就是通过任何一个网卡接收都可以。

3.然后服务器死循环调用recvfrom,recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);,没有数据则在套接字接收缓冲区等待队列上阻塞等待

udp客户端逻辑

1.命令行参数指明服务器端主机的任意一个网卡的ip地址,然后再指明端口,创建套接字socket(AF_INET, SOCK_DGRAM, 0);

2.用命令行参数准备好服务器通信套接字的地址结构体struct sockaddr_in,然后sendto发消息

udp发送消息过程分析

udp是没有发送缓冲区的,只有接收缓冲区,客户端调用sendto,会自动给套接字绑定端口,然后直接开始封装udp报文,然后交给IP层(其实就是调用ip协议的接口),传的参数就是udp报文和目标ip地址,ip层查路由表确定下一跳ip和发送网卡接口,封装IP报头,交给数据链路层处理(本质是调用以太网协议接口),传参IP报文和下一跳ip和发送网卡接口,网卡驱动会先查arp缓存,得到下一跳ip的mac地址,然后给报文加上mac头和crc校验,写进发送网卡对应的发送缓冲区,写网卡的TDT寄存器通知,然后网卡会DMA将数据读出,HVY转换信号,接口发送出去,咱们假设客户端是内网的一个主机,服务器部署在外网主机上,那这个下一跳很明显是路由器,路由器的网卡接口收到信号后,HVY信号转换,DMA写进网卡的接收缓冲区,触发硬件中断,cpu陷入内核,执行中断向量表中的中断方法,网卡驱动将数据读出,检查mac地址,然后看帧类型是IP帧,于是进行crc校验,没有问题就去掉mac头和crc校验,交给ip层,然后IP头的TTL减一,更改源ip为路由器的WAN口ip,并根据ip头中的首部长度,总长度,上层协议类型这些字段将udp头中的源port也改了(路由器除了维护路由表,还会维护地址转换表来辅助NAT,地址转换表里的对应关系是{源ip,源port,目的ip,目的port}和{改过的源ip,改过的源port,目的ip,目的端口}),改完后去查路由表确定下一跳ip和发送网卡,然后和之前一样发出去,这次发到公网了,服务器收到后不断解包到传输层(此时的底层应该是调用了udp协议的接口,将udp报文传过去),然后udp层会根据udp头里的端口拼出三元组{协议,目的ip,目的端口},然后根据OS维护的hash表找到对应udp套接字,将udp报文整个写进该套接字的接收缓冲区,然后唤醒接收缓冲区等待队列上的进程,recvfrom系统调用从接收缓冲区中读出一个完整的udp报文,然后去掉报头,将有效载荷写进recvfrom函数参数传来的的应用层缓冲区里,并填充参数传来的套接字地址和其大小

相关推荐
sakoba3 小时前
Docker学习其二(容器卷,Docker网络,Compose)
运维·网络·学习·docker·容器·基础
惜.己4 小时前
appium中urllib3.exceptions.LocationValueError: No host specified. 的错误解决办法
网络·appium
吉凶以情迁5 小时前
window服务相关问题探索 go语言服务开发探索调试
linux·服务器·开发语言·网络·golang
专注VB编程开发20年5 小时前
UDP受限广播地址255.255.255.255的通信机制详解
网络·udp·智能路由器
189228048616 小时前
NX947NX955美光固态闪存NX962NX966
大数据·服务器·网络·人工智能·科技
Sadsvit7 小时前
Linux 进程管理与计划任务
linux·服务器·网络
一碗白开水一7 小时前
【模型细节】FPN经典网络模型 (Feature Pyramid Networks)详解及其变形优化
网络·人工智能·pytorch·深度学习·计算机视觉
什么都想学的阿超8 小时前
【网络与爬虫 38】Apify全栈指南:从0到1构建企业级自动化爬虫平台
网络·爬虫·自动化
D-海漠9 小时前
安全光幕Muting功能程序逻辑设计
服务器·网络·人工智能
都给我10 小时前
可计算存储(Computational Storage)与DPU(Data Processing Unit)的技术特点对比及实际应用场景分析
运维·服务器·网络·云计算