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

【Linux】Socket编程——UDP版

📝前言:

这篇文章我们来讲讲Linux——udpsocket

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


这里写目录标题

  • 一、预备理论知识
  • 二、重要接口
    • 1. socket
    • 2. 发 / 收信息
    • 3. sockaddr结构体
    • 4. bind
    • 5. 本地和网络的序列转换
  • 三、理解通信流程
  • 四,实操案例
    • 1. 单词翻译
    • 2. 简单群聊

一、预备理论知识

  • 计算机之间通过网络进行数据的传输(Socket通信),本质上是:两个计算机的两个进程间进行通信(进程是人的代表)
    • 两个网络进程间通信,看到的同一份资源是网路
    • 每个数据的发送和接收都必须经过套接字(socket)
      • 套接字负责维护网络通信的各种信息(如本地/远程地址、端口、协议类型等)。
      • 操作系统通过套接字来区分不同的网络连接和数据流。
      • 只有通过套接字,操作系统才能知道数据要发到哪里、从哪里收。
    • IP 用来标识是哪一台主机,Port端口号用来标识是主机上的哪一个进程
    • 一个端口号只能被一个进程占用,但是一个进程可以绑定多个端口号
  • 如何理解绑定?
    • 我们可以认为传输层有一张元素为:<端口号, 进程链表> 的hash
    • 当一个进程要和一个端口号绑定的时候,就会被加入到对应端口号的链表当中
    • 当收到网络传输过来的报文以后,就会提取(传输层有多种协议)其中的目的端口,然后去hash里面找到对应的进程(把数据交给对应的进程)

二、重要接口

1. socket

创建网络套接字

int socket(int domain, int type, int protocol);
  • domain:协议族,确定网络层使用的协议
    • 可以选:AF_INET(IPv4 协议),AF_INET6(IPv6 协议)…
  • type:套接字类型(常用的如下)
    • SOCK_DGRAM:无连接的 UDP 协议
    • SOCK_STREAM:面向连接的 TCP 协议
  • protocol:通常用 0 就行

TCP 是面向连接、可靠的字节流传输协议(需三次握手建立连接,数据无丢失、有序到达),而 UDP 是无连接、不可靠的数据包传输协议(无需建立连接,数据可能丢失、无序,但是 更简单,更快)

2. 发 / 收信息

向指定的IP和端口发消息

// ssize_t 是一个有符号整数类型
ssize_t sendto(int sockfd,          // 已绑定的套接字描述符const void *buf,     // 要发送数据的缓冲区size_t len,          // 数据长度 int flags,           // 发送标志const struct sockaddr *dest_addr,  // 目标地址结构指针socklen_t addrlen);  // 目标地址结构的长度

从指定 IP 和端口接收信息,将数据从内核层的Socket接收到用户层的缓冲区

ssize_t recvfrom(int sockfd,  // 从哪个套接字接收(远端传来的数据是先存到套接字缓冲区的)void *buf,   size_t len, int flags, struct sockaddr *src_addr,  // 接收到的数据来自哪个地址(对端地址)socklen_t *addrlen);

两个sockfd是一个东西,通过它向服务端发送数据,也通过它接收服务端返回的数据。

3. sockaddr结构体

struct sockaddr是一个C语言版本的“基类”(我们在传递参数的时候,要强转成:struct sockaddr*
对于
绑定IP和端口号就是填充对应不同协议的结构体,填充IPV4的struct sockaddr_in

  • sin_family:地址家族
  • sin_port :端口号
  • sin_addr.s_addr:IP(在sockaddr_in里面专门用一个sin_addr来管理IP,里面有本地主机以 uint32_t 存储的IPs_addr

4. bind

bind() 是一个核心系统调用,用于将套接字(socket)与特定的 IP 地址和端口号绑定,从而让程序能够在指定的地址和端口上接收网络请求。

int bind(int sockfd,  // 套接字描述符const struct sockaddr *addr,  // sockaddr 结构体socklen_t addrlen);  // addr 结构体的长度

5. 本地和网络的序列转换

关于大小端存储:
不同的机器,可能会用不同的方案(大小端)来存储IP,为了统一,网络规定,发到网络上的必须是大端的。所以需要转换。

  • 主机转网络
    • htons接口:将 16 位端口号 从主机字节序转换为网络字节序(大端序,内部会自行判断是否需要转换)
    • inet_pton:转IP,将字符串形式的IP地址转换成网络序列
  • 网络转主机(是上面的逆操作):
    • ntohs:转端口
    • inet_ntop:转 IP

示例:

// 处理 IPv4
struct in_addr ipv4_addr;
if (inet_pton(AF_INET, "192.168.1.1", &ipv4_addr) == 1) {// 转换成功
}// 处理 IPv6
struct in6_addr ipv6_addr;
if (inet_pton(AF_INET6, "2001:db8::1", &ipv6_addr) == 1) {// 转换成功
}

转换好的结果会被存在ipv6_addr这个结构体里面

三、理解通信流程

服务端工作流程

  • 创建套接字:服务端首先创建一个 UDP 套接字
  • 绑定地址:服务端将套接字绑定到特定的 IP 地址和端口
  • 接收数据:服务端使用 recvfrom() 函数从该套接字接收数据

客户端工作流程

  • 创建套接字:客户端创建自己的 UDP 套接字
  • 准备目标地址:客户端构造包含服务端 IP 地址和端口的 sockaddr_in 结构体
  • 发送数据:客户端调用 sendto() 函数,指定目标地址(通过绑定的 sockaddr_in),将数据发送到服务端

关键点

  • 服务端绑定:服务端必须绑定到固定地址,因为客户端要明确目标
    • 为了让服务端套接字监听主机的所有可用网络接口,我们可以绑定INADDR_ANY(假如服务器有多个网卡,多个IP,绑定INADDR_ANY后,客户端无论是通过哪个IP访问,服务器都能接收)
  • 客户端不需要绑定:在 UDP 通信中,客户端通常不需要显式绑定地址,第一次sendto时,操作系统会自动分配一个临时端口

套接字通信的工作原理
客户端和服务端的套接字是完全不同的实体。
当客户端调用 sendto() 时:

  1. 操作系统通过 sockfd 参数知道数据从哪个本地套接字发出
  2. 通过 dest_addr 参数知道数据要发送到哪个远程地址
  3. 操作系统自动为该套接字分配一个临时端口(如果尚未分配)
  4. 数据包中包含源 IP、源端口、目标 IP 和目标端口

当服务端接收到数据包时:

  1. 操作系统根据目标 IP 和端口,将数据包路由到绑定到该地址的套接字
  2. (对应目标IP和端口的) 服务端可以通过 recvfrom() 获取发送方的 IP 和端口信息
  3. 服务端可以使用这些信息,通过同一个或不同的套接字回复客户端

sendto函数里面,我们要传入要发送的信息的buffer,这个信息先被复制到套接字 sockfd 里面,然后(如果客户端没有绑定IP和端口,系统会自动分配)将信息和操作系统自动分配的源IP和端口,以及目标IP和端口(以及添加UDP头部…等等,也就是添加报头)一起打包成一个数据包然后再发送到 sockaddr_in 指向的目标地址

客户端应用程序    →    客户端套接字缓冲区    →    网络    →    服务端套接字缓冲区    →    服务端应用程序
(数据)            (数据复制)               (数据包)     (数据复制)               (数据)

四,实操案例

1. 单词翻译

代码有点太长了,如果有需要,可以访问我的Github
注意:

  • 公网IP其实没有配置到我们机器的IP上,无法被bind
  • 本地环回:要求c、s必须在一台机器上,表明我们是本地通信,client发送的数据,不会被推送到网络而是在OS内部,转一圈直接交给对应的服务器端(常用于网络代码的测试)

运行结果:

客户端
在这里插入图片描述
服务端:在这里插入图片描述

2. 简单群聊

获取代码 → 点击


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

http://www.lryc.cn/news/615027.html

相关文章:

  • 第八章 SQL编程系列-Oracle慢SQL优化实战:从执行计划到索引设计的深度解析
  • UE蓝图节点Add Impulse和Add Torque in Radians
  • FMS 2025存储峰会获奖技术全景解读
  • 【线性代数】目录
  • 7、docker |其余命令
  • Datawhale+AI夏令营_让AI读懂财报PDF task2深入赛题笔记
  • RK3568笔记九十九:基于FFMPEG拉取RTSP流MPP硬解码视频显示
  • 使用Navicat备份数据库MySQL、PostGreSQL等
  • (一)React复习小满(userImmer/userMemo/useContext/userCallback/userRef)
  • 【SQL进阶】用EXPLAIN看透SQL执行计划:从“盲写“到“精准优化“
  • ABP VNext + Akka.NET:高并发处理与分布式计算
  • c++ opencv调用yolo onnx文件
  • 2025-08-09通过授权码的方式给exe程序充值
  • jQuery 零基础学习第一天
  • 计算BERT-BASE参数量
  • 【数据分享】各省农业土地流转率(2010-2023)
  • 安全合规3--防火墙
  • 光伏面板损伤检出率↑91%!陌讯多模态识别算法在无人机巡检的落地实践
  • 建筑物实例分割数据集-9,700 张图片 城市规划与发展 灾害评估与应急响应 房地产市场分析 智慧城市管理 地理信息系统(GIS) 环境影响评估
  • Android MVP架构详解:从理论到实践
  • leetcode2090:半径为K的子数组平均值(定长滑动窗口)
  • C# 使用iText获取PDF的trailer数据
  • 【lucene】HitsThresholdChecker命中阈值检测器
  • 【Datawhale AI夏令营第三期】多模态RAG
  • 《Learning To Count Everything》论文阅读
  • 论文阅读-ZeroDCE和ZeroDCE++
  • OpenCV图像裁剪与 ROI 操作
  • Kubernetes 集群密钥与机密管理方案对比分析:Vault、Sealed Secrets 与 AWS KMS
  • vue+flask山西非遗文化遗产图谱可视化系统
  • 【Linux】Tomcat