Linux网络部分——基于UDP的四大接口,实例应用,常考汇总,完整服务端、客户端设计流程+代码

@bit::Shadow
✧(≖ ◡ ≖✿

目录

四大接口简解(基于Udp的实现)

套接字socket()创建的含义

进行bind()的含义

recvfrom的含义

sendto()的含义

四大接口详解

[一、总览:四个函数在 UDP 通信中的分工](#一、总览:四个函数在 UDP 通信中的分工)

二、逐个深入解析

[1. socket() ------ 创建通信端点](#1. socket() —— 创建通信端点)

含义:

技术本质:

在你代码中的位置:

快递类比:

关键点:

[2. bind() ------ 绑定地址和端口](#2. bind() —— 绑定地址和端口)

含义:

技术本质:

在你代码中的位置:

快递类比:

关键点:

[3. recvfrom() ------ 接收数据并获取发送方](#3. recvfrom() —— 接收数据并获取发送方)

含义:

技术本质:

在你代码中的位置:

快递类比:

关键点:

[4. sendto() ------ 发送数据到指定地址](#4. sendto() —— 发送数据到指定地址)

含义:

技术本质:

在你代码中的位置:

快递类比:

关键点:

三、一个完整的交互流程(串起来)

四、核心要点总结(面试/考试常考)


gitee服务端、客户端(待链接)🔗

四大接口简解(基于Udp的实现)

套接字socket()创建的含义

向内核申请一个"网络通信端点",返回一个文件描述符(fd),后续所有网络操作都通过它来标识这个通信通道。

进行bind()的含义

把套接字与一个本地 IP 地址和端口号关联起来,告诉内核"这个套接字负责接收发往这个 IP:端口 的数据"。

recvfrom的含义

从套接字的接收缓冲区中取出一个 UDP 数据报,同时把发送方的 IP 地址和端口号返回给调用者。

sendto()的含义

把数据封装成 UDP 数据报,发送到指定的目标 IP 地址和端口。

四大接口详解

这个问题问得非常系统,相当于把 UDP 通信的完整生命周期 串起来了。我结合你之前的代码和实际运行过程,把 socket()bind()recvfrom()sendto() 这四个核心系统调用,用**"寄快递"** 的比喻 + 技术原理解析,一次性讲透它们的含义和关系。


一、总览:四个函数在 UDP 通信中的分工

函数 一句话含义 快递类比 调用方
socket() 创建通信端点(获得一个"信筒") 向邮局申请一个专属信箱 服务端和客户端都需要
bind() 绑定地址和端口(给信筒贴上门牌号) 在信箱上写上地址和收件人 服务端必须,客户端可选
recvfrom() 接收数据 + 获取发送方地址(收信并看寄件人) 从信箱取信,同时记住寄件人地址 服务端/客户端都可能用
sendto() 发送数据到指定地址(寄信) 把信寄给指定地址的收件人 服务端/客户端都可能用

二、逐个深入解析

1. socket() ------ 创建通信端点

含义:

向内核申请一个"网络通信端点",返回一个文件描述符(fd),后续所有网络操作都通过它来标识这个通信通道。

技术本质:
  • 内核创建 struct socketstruct sock 对象。

  • 分配发送/接收缓冲区。

  • 关联协议处理函数(如 UDP 的 udp_sendmsg / udp_recvmsg)。

  • 返回一个非负整数 fd。

在你代码中的位置:
cpp 复制代码
// UdpServer 的 Init() 方法内部(推测)
_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// 此时 _sockfd 是一个有效的套接字,但还没有绑定地址
快递类比:

你去邮局租了一个信箱 (套接字),得到一个信箱编号 (fd)。现在你有了收发信的能力,但还没有告诉邮局你的地址(bind),所以别人还无法给你寄信。

关键点:
  • socket() 本身不涉及任何网络通信,只是创建了一个内核对象。

  • UDP 和 TCP 的 socket() 调用方式不同(SOCK_DGRAM vs SOCK_STREAM)。

  • socket() 成功后,fd 是"未绑定、未连接"的,需要后续操作赋予它具体功能。


2. bind() ------ 绑定地址和端口

含义:

把套接字与一个本地 IP 地址和端口号关联起来,告诉内核"这个套接字负责接收发往这个 IP:端口 的数据"。

技术本质:
  • struct sockaddr_in(IPv4 地址 + 端口)与套接字绑定。

  • 内核在 UDP 端口表中记录这个映射关系。

  • 当数据包到达该 IP:端口 时,内核能快速找到对应的套接字,将数据放入其接收缓冲区。

在你代码中的位置:
cpp 复制代码
// UdpServer 的 Init() 方法内部(推测)
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;   // 0.0.0.0,监听所有网卡
addr.sin_port = htons(port);

if (bind(_sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
    // 错误处理
}
快递类比:

你在信箱上贴了一个地址标签(比如"北京市朝阳区XX路18号 8888房间")。这样邮递员(内核)才知道,发往这个地址的信应该投递到你的信箱里。

关键点:
  • 服务端必须 bind(),否则客户端不知道往哪个地址发数据。

  • 客户端可以不用 bind(),内核会自动分配一个临时端口(49152-65535)。

  • INADDR_ANY0.0.0.0)表示监听本机所有网卡的该端口。

  • 端口号需要用 htons() 转换字节序(大端 → 网络字节序)。



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

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

3. recvfrom() ------ 接收数据并获取发送方

含义:

从套接字的接收缓冲区中取出一个 UDP 数据报,同时把发送方的 IP 地址和端口号返回给调用者。

技术本质:
  • 阻塞(或非阻塞)等待数据到达。

  • 从内核的接收缓冲区拷贝数据到用户空间的 buf

  • 同时将发送方的地址结构(struct sockaddr_in)填充到 src_addr 参数中。

  • UDP 是数据报模式 :每次 recvfrom 返回一个完整数据报。

在你代码中的位置:
cpp 复制代码
// 你之前问过的 recvfrom
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);

ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0,
                     (struct sockaddr*)&client_addr, &addr_len);
if (n > 0) {
    // 此时 client_addr 中保存了发送方的 IP 和端口
    // 可以用 sendto 回复这个地址
}
快递类比:

你从信箱里取出一封信(数据),信上盖着寄件人的地址戳src_addr)。你不仅读到了信的内容,还知道了是谁寄来的------这样你就可以回信了。

关键点:
  • 这是 UDP 服务端获取客户端地址的唯一途径(因为 UDP 无连接)。

  • addr_len输入输出参数:调用前传入缓冲区大小,调用后内核回填实际地址长度。

  • 如果 buf 小于数据报大小,剩余数据会被丢弃(UDP 的特有坑)。


4. sendto() ------ 发送数据到指定地址

含义:

把数据封装成 UDP 数据报,发送到指定的目标 IP 地址和端口。

技术本质:
  • 将用户空间的 buf 拷贝到内核。

  • 封装 UDP 头(源端口、目标端口、长度、校验和)。

  • 交给 IP 层(加上 IP 头),然后通过路由表决定从哪个网卡发出。

  • UDP 是不可靠 的:sendto() 返回成功只表示数据已交给 IP 层,不保证对端已收到。

在你代码中的位置:
cpp 复制代码
// 你之前用过的 sendto
ssize_t ret2 = sendto(_sockfd, buf, sizeof(buf) - 1, 0,
                      (const sockaddr *)&remote, sizeof(remote));
//                                               ^^^^^^^^^^
//                                               注意:是 remote 的大小,不是 socklen_t 类型的大小
快递类比:

你写好了信,贴上邮票,然后根据寄件人地址(dest_addr)把信投递出去 。你投进邮筒(sendto)后,只表示信已经进了邮局系统,但不保证对方一定收到(UDP 的不可靠性)。

关键点:
  • 目标地址必须正确 (IP + 端口),否则会返回 ENETUNREACHEHOSTUNREACH

  • 数据长度不能超过 65507 字节(UDP 最大载荷),否则返回 EMSGSIZE

  • dest_addr 可以是:

    • 客户端发数据给服务器时:服务器地址

    • 服务器回复客户端时:从 recvfrom 获取的 client_addr


三、一个完整的交互流程(串起来)

假设有一个 UDP 服务器(IP 192.168.1.100,端口 8888)和一个客户端(IP 192.168.1.10,临时端口 52345):

cpp 复制代码
【服务端】                                          【客户端】
1. socket() → 创建套接字 fd                          1. socket() → 创建套接字 fd
   (获得一个信箱)                                     (也获得一个信箱)
   
2. bind() → 绑定 INADDR_ANY:8888                     (客户端不需要 bind)
   (贴上地址标签:"8888 房间")                        (内核自动分配临时端口 52345)

3. recvfrom() → 阻塞等待数据                         3. sendto() → 发送数据到 192.168.1.100:8888
   (坐在信箱前等信)                                   (把信寄给服务端地址)

4. recvfrom() 返回,收到数据,                        (客户端继续)
   同时获取到客户端地址(192.168.1.10:52345)
   (取信并看到寄件人地址)

5. sendto() → 回复数据到客户端地址                    5. recvfrom() → 接收服务端回复
   (回信给寄件人)                                    (从自己的信箱取回信)

☆☆客户端、服务端通信的特异性的确定

./udpClient IP port 运行可执行程序

需不需要显示bind?看似需要实则相反!

☆☆☆要回答此问题必须明确前提:bind实现通信的原理 {IP,port,目标IP,目标port} 只要两组这四元组不完全相同,UDP通信就完全没有问题。

IP:服务端默认接受所有IP请求。为区分不同的客户端所以每个"客户"必须保证不同IP(若有两个网卡,即一台机器有了两个IP,可被客户端区分,允许分别请求)。

port:服务端需要指定端口号。客户端链接指定的服务端端口号,客户端的port由系统自动生成。(recvfrom 、 sendto时自动调用connect()分配)

四、核心要点总结(面试/考试常考)

函数 服务端是否必须 客户端是否必须 核心作用
socket() ✅ 是 ✅ 是 创建套接字端点
bind() 必须(固定端口) ❌ 可选(不调用则内核分配临时端口) 绑定本地地址和端口
recvfrom() ✅ 必须(接收请求) ❌ 可选(看是否需要接收回复) 接收数据报 + 获取发送方地址
sendto() ✅ 必须(回复客户端) ✅ 必须(发送请求) 向指定地址发送数据报

感谢支持,长期连载

欢迎关注

相关推荐
深念Y1 小时前
老Flyme 官方 root 开启 ADB TCP 方案
linux·数据库·网络协议·tcp/ip·adb·智能手机·emmc
小此方2 小时前
Re:Linux系统篇(五十二)线程篇 · 五:直击 Linux 内核底层:TCB 与 LWP 一体两面全解析(附自定义线程封装)
linux·运维·驱动开发
ltl10 小时前
校验和与数据完整性:CRC32C、xxHash 与端到端校验
linux
我是小灰灰吖10 小时前
Ubuntu 22.04 设置 CPU 性能模式:提升系统响应速度
linux·ubuntu
ltl10 小时前
持续性能分析:Parca、Pyroscope、Grafana Beyla
linux
ltl10 小时前
Direct I/O 与 O_DIRECT:绕过缓存的得与失
linux
小洁忘了怎么分身11 小时前
多样本空间转录组 Harmony 整合与标签转移指南
网络·r语言·生信分析
盐焗鹌鹑蛋11 小时前
【Linux】进程控制
linux
盐焗鹌鹑蛋13 小时前
【Linux】进程程序替换
linux