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

【Linux-传输层协议UDP】再谈端口号+UDP协议+深度理解UDP

前沿

传输层

负责数据能够从发送端传输接收端

再谈端口号

端口号(Port)标识了一个主机上进行通信的不同的应用程序

在TCP/IP协议中,用“源IP”,“源端口号”,“目的IP”,“目的端口号”,“协议号”这样一个五元组来标识一个通信(可以通过 netstat-n 查看);

 

端口号范围划分

端口号是一个 16 位的整数,它的取值范围是 0~65535。

  • 0~1023:知名端口号。比如 HTTP,FTP,SSH 等这些广为使用的应用层协议,它们的端口号 都是固定的。

  • 1024~65535:操作系统动态分配的端口号。客户端程序的端口号就是由操作系统从这个范围 分配的,允许用户手动绑定。

认识知名端口号

有些服务器是非常常用的,为了使用方便,人们约定一些常用的服务器,都是用以下这些固定的端口号:

  • ssh 服务器使用 22 端口

  • ftp 服务器使用 21 端口

  • telnet 服务器使用 23 端口

  • http 服务器使用 80 端口

  • https 服务器使用 443

执行下面的命令,可以看到知名端口号

我们自己写一个程序使用端口号时,要避开这些知名端口号

 

两个问题

一个进程是否可以bind多个端口号?--》可以

一个端口号是否可以被多个进程bind---〉不可以

UDP协议

UDP协议端格式

思考:如何将报头和有效载荷进行分离(封装)?如何将有效载荷进行分用


 

  • 16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度

  • 如果校验和出错,就会直接丢弃

UDP的特点

UDP 传输的过程类似于寄信.

  • 无连接:知道对端的IP 和端口号就直接进行传输,不需要建立连接;

  • 不可靠:没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP 协议层也不会给应用层返回任何错误信息;

  • 面向数据报:不能够灵活的控制读写数据的次数和数量;

面向数据报

应用层交给UDP多长的报文,UDP 原样发送,既不会拆分,也不会合并;

用 UDP 传输 100个字节的数据:

  • 如果发送端调用一次 sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节;而不能循环调用10次recvfrom,每次接收10个字节;

UDP 的缓冲区

  • UDP 没有真正意义上的 发送缓冲区.调用sendto 会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作;

  • UDP 具有接收缓冲区.但是这个接收缓冲区不能保证收到的UDP 报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的 UDP 数据就会被丢弃;

UDP 的socket 既能读,也能写,这个概念叫做 全双工

UDP 使用注意事项

我们注意到,UDP协议首部中有一个16位的最大长度.也就是说一个UDP能传输的数据最大长度是64K(包含UDP 首部).

然而 64K 在当今的互联网环境下,是一个非常小的数字.

如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装;

基于UDP 的应用层协议

  • NFS:网络文件系统

  • TFTP:简单文件传输协议

  • DHCP:动态主机配置协议

  • BOOTP:启动协议(用于无盘设备启动)

  • DNS:域名解析协议

当然,也包括你自己写UDP 程序时自定义的应用层协议;

深刻理解UDP

1.UDP报头

操作系统内可能同时存在大量的报文,有的报文正在被向上交付,有的报文正在被向下交付;这么多的报文,所以操作系统要对报文进行各种管理--->先描述,在组织

struct udphdr 是C语言中用于描述UDP(用户数据报协议)头部的一个结构体。UDP是OSI模型中的传输层协议,它提供了无连接的数据包传送服务。以下是 struct udphdr 的一个典型定义,通常在Linux系统的头文件 <netinet/udp.h> 中可以找到:

struct udphdr {
    uint16_t source;  /* 源端口号 */
    uint16_t dest;    /* 目的端口号 */
    uint16_t len;     /* UDP数据报长度 */
    uint16_t check;   /* 校验和 */
};

2.对报文的理解

UDP封装的过程,首先我们知道协议栈有四层:应用层,传输层,网络层,数据链路层;从应用层到传输层需要对数据进行封装,那么UDP是如何一层层封装的呢

首先我们需要认识一个数据结构struct sk_buff

struct sk_buff 是Linux内核网络子系统中的一个核心数据结构,它用于表示网络层的数据包缓冲区。sk_buff 代表一个 “socket buffer”,它包含了内核处理网络数据包所需的所有信息,包括网络层和传输层头部、数据负载、元数据以及用于管理缓冲区的各种指针。

以下是 struct sk_buff 的简化版本,用于展示其主要字段:

struct sk_buff {
    // 指向skb_shared_info结构的指针,该结构包含了数据包共享信息
    struct skb_shared_info *shinfo;

    // 指向skb_memo结构的指针,该结构用于存储skb的元数据
    struct skb_memo *memo;

    // 指向skb对应的网络设备结构的指针
    struct net_device *dev;

    // 指向skb所属的套接字的指针
    struct sock *sk;

    // 指向skb的传输控制块的指针
    struct sk_buff *next;
    struct sk_buff *prev;

    // 指向skb的数据缓冲区的指针
    unsigned char *head;
    unsigned char *data;
    unsigned char *tail;
    unsigned char *end;

    // skb的长度
    unsigned int len;
    unsigned int data_len;

    // skb的类型
    unsigned int type;

    // skb的协议
    __be16 protocol;

    // skb的标记
    __u32 mark;

    // skb的hash值
    __u32 hash;

    // skb的优先级
    __u32 priority;

    // 其他字段...
};

 

字段名类型描述
shinfo指针(struct skb_shared_info*指向skb_shared_info结构,包含数据包的共享信息,如片段信息和引用计数。
dev指针(struct net_device*指向发送或接收此数据包的网络设备结构。
sk指针(struct sock*指向数据包所属的套接字结构。
head指针(unsigned char*指向数据包缓冲区的开始。
data指针(unsigned char*指向数据包中数据的开始。
tail指针(unsigned char*指向数据包中数据的末尾。
end指针(unsigned char*指向数据包缓冲区的末尾。
lenunsigned int数据包的总长度(包括所有头部)。
data_lenunsigned int数据部分的长度(不包括头部)。
typeunsigned char数据包的类型,用于内核处理。
protocol__be16数据包的协议字段,通常是IP层头部中的协议字段。
markunsigned int数据包的标记,用于QoS和其他目的。
hashunsigned int数据包的哈希值,用于快速查找。

UDP(用户数据报协议)报文的封装过程涉及到在传输层将数据打包成UDP数据报,并将其传递给网络层以发送到目的地

以下是将UDP报文封装过程:

阶段操作描述
应用层发送数据应用程序通过UDP套接字发送数据。
传输层创建UDP头部内核创建一个UDP头部(struct udphdr)。
传输层填充UDP头部源端口、目的端口、长度和校验和字段被填充。
传输层创建skb缓冲区内核创建一个skb缓冲区(struct sk_buff)来管理数据。
传输层附加数据到skb应用程序数据被附加到skb缓冲区的数据部分。
传输层计算校验和如果启用了校验和,则计算UDP伪头部、头部和数据。
网络层添加IP头部skb缓冲区被传递给网络层,并添加IP头部(struct iphdr)。
网络层填充IP头部源IP地址、目的IP地址、协议类型(17表示UDP)等被填充。
链路层添加链路层头部skb缓冲区被传递给链路层,并添加链路层头部(例如以太网头部)。
链路层填充链路层头部目的MAC地址、源MAC地址和类型(对于IP数据包是0x0800)等被填充。
发送发送数据包最终的数据包(包含链路层、IP层和UDP层头部)被发送到网络。
应用数据
+-------------------+
|                   |
|     应用数据      |
|                   |
+-------------------+
       |
       | (通过UDP套接字发送)
       v
UDP头部 (struct udphdr)
+-------------------+
| 源端口 | 目的端口 | 长度 | 校验和 |
+-------------------+
       |
       | (skb缓冲区管理)
       v
IP头部 (struct iphdr)
+-------------------+
| 版本 | 头部长度 | 服务类型 | 总长度 |
| 标识 | 标志 | 片偏移 | 生存时间 |
| 协议 | 校验和 | 源IP地址 | 目的IP地址 |
+-------------------+
       |
       | (网络层处理)
       v
链路层头部 (例如以太网头部)
+-------------------+
| 目的MAC | 源MAC | 类型 |
+-------------------+
       |
       | (发送到网络)
       v

相关文章:

  • AI 赋能软件开发:从工具到思维的全面升级
  • 【网络协议详解】——QOS技术(学习笔记)
  • Redis-缓存穿透击穿雪崩
  • paimon---同步mysql数据到paimon表中
  • 基于Spring Boot的国产动漫网站的设计与实现(LW+源码+讲解)
  • TDengine 数据对接 EXCEL
  • 记一次Spring Boot应用中数据库连接阻塞问题排查过程
  • 重生之我在学Vue--第8天 Vue 3 UI 框架(Element Plus)
  • 深度学习——Diffusion Model学习,扩散模型
  • deepseek使用记录21——游击战略问题
  • python 中用到的文件操作
  • 从运营出发:打造更适配当下营商环境的一对一直播系统源码
  • MySQL(第3周)-database命令
  • Python自动点击器开发教程 - 支持键盘连按和鼠标连点
  • 多线程(二)
  • 蓝桥杯真题0团建dfs+哈希表/邻接表
  • 统计登录系统10秒内连续登录失败超过3次的用户
  • 看 MySQL InnoDB 和 BoltDB 如何写磁盘
  • Vivado IP核之定点数累加Accumulator使用说明
  • vscode接入DeepSeek 免费送2000 万 Tokens 解决DeepSeek无法充值问题
  • 用cms做单页网站怎么做/佛山seo优化
  • 做视频网站推广/市场营销计划书模板
  • 网站模板带后台/百度客服平台
  • 有了网站怎么做app/百度指数排名
  • 服务器可以做网站吗/搜索引擎优化核心
  • wordpress发布文章关键词/seo作弊