11 udp 发送数据的流程梳理

news2024/11/18 8:24:10

前言

呵呵 之前曾经看到过 湖光大佬 的 tcp 的流程梳理

呵呵 很高深 有很多不明白的地方, 不光是涉及到 linux 网络处理本身的东西, 还涉及到了 tcp协议 的一些具体的实现, 是非常的复杂

这里之前 在 0voice/linux_kernel_wiki 上面看到了网络协议栈部分的梳理

呵呵 自己也稍微走了一下 流程, 这里稍微记录一下

主要核心的内容包含了, 用户数据传递过来, 数据包的封装, 然后到 数据包发送到驱动层 的这个流程, 当然 是没有上面的 0voice/linux_kernel_wiki 网络协议栈 部分内容详细, 以及准确 

记录于 2022.05.02 

测试环境 : linux 4.10.14 + qemu 2.5.0 

测试用例

测试用例, 拷贝自网络, 但是忘机记录具体的地址信息了, ^_^ 

udp 服务器 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8082
#define BUFSIZE 512
char buf[BUFSIZE+1];
int main()
{
  //第 1 步 创建套接字
  int sockfd=socket(AF_INET,SOCK_DGRAM,0);
  //第 2 步 设置地址结构体
  struct sockaddr_in svraddr;
  svraddr.sin_family=AF_INET;//使用 internet 协议
  svraddr.sin_port=htons(PORT);
//  inet_aton("0.0.0.0",&svraddr.sin_addr);
  inet_aton("192.168.0.103",&svraddr.sin_addr);
  //第 3 步 绑定
  int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
  if(ret<0){printf("cannot bind!\r\n");exit(-1);};
  while(1)
  {
        struct sockaddr_in cli;
        int len=sizeof(cli);
    int z=recvfrom(sockfd,buf,BUFSIZE,0,(struct sockaddr*)&cli,&len);//第 6 步 读取套接字
    buf[z]='\0';
    printf("%s\r\n",buf);//打印
  }

}

udp 客户端 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define PORT 8082
#define BUFSIZE 512
char buf[BUFSIZE+1];
int main()
{
  //第 1 步 创建一个体套接字
  int sockfd=socket(AF_INET,SOCK_DGRAM,0);
  //第 2 步 设置 addr 结构体
  struct sockaddr_in svraddr;
  svraddr.sin_family=AF_INET;//使用 internet 协议
  svraddr.sin_port=htons(PORT);
  inet_aton("0.0.0.0",&svraddr.sin_addr);
  //第 3 步 连接服务器
  //connect(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
  buf[0] = '1';
  buf[1] = '2';
  buf[2] = '3';
//  while(1)
//  {
  sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&svraddr,sizeof(svraddr)); //第 4 步 向套接字中写入字符串
//  }
}

如上 客户端发送了 三个字节 的数据 

以太网头部, ip头部, udp 头部 合计为 14 + 20 + 8 = 42 字节, 因此 客户端给服务器发送的数据报文长度为 45 字节

//Mac头部,总长度14字节  
typedef struct _eth_hdr  
{  
    unsigned char dstmac[6]; //目标mac地址  
    unsigned char srcmac[6]; //源mac地址  
    unsigned short eth_type; //以太网类型  
} eth_hdr;
//IP头部,总长度20字节  
typedef struct _ip_hdr  
{  
    #if LITTLE_ENDIAN  
    unsigned char ihl:4;     //首部长度  
    unsigned char version:4, //版本   
    #else  
    unsigned char version:4, //版本  
    unsigned char ihl:4;     //首部长度  
    #endif  
    unsigned char tos;       //服务类型  
    unsigned short tot_len;  //总长度  
    unsigned short id;       //标志  
    unsigned short frag_off; //分片偏移  
    unsigned char ttl;       //生存时间  
    unsigned char protocol;  //协议  
    unsigned short chk_sum;  //检验和  
    struct in_addr srcaddr;  //源IP地址  
    struct in_addr dstaddr;  //目的IP地址  
} ip_hdr;
//UDP头部,总长度8字节  
typedef struct _udp_hdr  
{  
    unsigned short src_port; //远端口号  
    unsigned short dst_port; //目的端口号  
    unsigned short uhl;      //udp头部长度  
    unsigned short chk_sum;  //16位udp检验和  
} udp_hdr;

这是一段相似的报文, 不过 服务器 和 客户端 主机和我们调试的主机不同 

Frame 654: 45 bytes on wire (360 bits), 45 bytes captured (360 bits) on interface en0, id 0
Ethernet II, Src: Apple_c9:48:f9 (38:f9:d3:c9:48:f9), Dst: VMware_4e:80:29 (00:0c:29:4e:80:29)
    Destination: VMware_4e:80:29 (00:0c:29:4e:80:29)
    Source: Apple_c9:48:f9 (38:f9:d3:c9:48:f9)
    Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.0.103, Dst: 192.168.0.20
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
    Total Length: 31
    Identification: 0xaf64 (44900)
    Flags: 0x00
    Fragment Offset: 0
    Time to Live: 64
    Protocol: UDP (17)
    Header Checksum: 0x499e [validation disabled]
    [Header checksum status: Unverified]
    Source Address: 192.168.0.103
    Destination Address: 192.168.0.20
User Datagram Protocol, Src Port: 53260, Dst Port: 8082
    Source Port: 53260
    Destination Port: 8082
    Length: 11
    Checksum: 0x2a3b [unverified]
    [Checksum Status: Unverified]
    [Stream index: 5]
    [Timestamps]
    UDP payload (3 bytes)
Data (3 bytes)
    Data: 313233
    [Length: 3]

udp 发送数据包的流程 

用户调用 sendto 系统调用 和 内核进行交互, 传输的 buf 的内容是用户程序中传入的 buf 前三个字节分别为 '1', '2', '3' 

我们这里不会关注 整个流程, 我们只会关注一部分的点 

1. 用户数据 封装 msghdr 的地方 

2. msghdr 封装 skb  的地方

3. 封装 用户数据 的地方

4. 封装 ip 头的地方

5. 封装 udp 头的地方 

6. 封装 eth 头的地方

7. 封装好了 skb 之后内核传递数据给 driver 的地方

1. 用户数据 封装 msghdr 的地方 

用户程序 调用系统调用传入的输入 

buf 为 0x601080, len 为 3, 这三个字节分别对应于 '1', '2', '3' 

然后根据传入的 buf 创建了 msghdr 向下交互 

(gdb) x/2gx 0x601080
0x601080:	0x0000000000333231	0x0000000000000000

2. msghdr 封装 skb  的地方 

from 对应的是传入的 msghdr 

getfrag 对应的是将数据填充到 skb 中的函数 

3. 封装 用户数据 的地方

 将 msghdr 中封装的 buf 的数据拷贝到 skb 的指定位置[预留 ip 头 + udp 头的长度] 

这里的偏移为 20 + 8 = 28 

这里 拷贝了 '1', '2', '3' 到 skb 中

4. 封装 ip 头的地方

封装了 skb 之后, 会更新 skb 的 iphdr 的相关信息 

iphdr 数据填充之后, 我们拆解一下关键的信息项, 注意 dump 出来的内存为小端序 

source_ip 在 0xffff88007f44101c 的位置, 值为 0x0100007f = 127.0.0.1 

dst_ip 在 0xffff88007f441020 的位置, 值为 0x0100007f = 127.0.0.1 

protocol 为 0xffff88007f441019 的位置 值为 0x11 = 17 = udp 

ttl 为 0xffff88007f441018 的位置 值为 0x40 = 64 

(gdb) x/20gx 0xffff88007f441000
0xffff88007f441000:	0xffff88007f441400	0x0000000000000040
0xffff88007f441010:	0x00406b6500400045	0x0100007f00401140
0xffff88007f441020:	0x000000000100007f	0x00333231000001f8
0xffff88007f441030:	0x0000000000000008	0x0000000400000003
0xffff88007f441040:	0x0000000000000238	0x0000000000400238
0xffff88007f441050:	0x0000000000400238	0x000000000000001c
0xffff88007f441060:	0x000000000000001c	0x0000000000000001
0xffff88007f441070:	0x0000000500000001	0x0000000000000000
0xffff88007f441080:	0x0000000000400000	0x0000000000400000
0xffff88007f441090:	0x0000000000000924	0x0000000000000924

5. 封装 udp 头的地方 

封装了 skb 之后会将 skb 从 udp 层传递到 ip 层, 在此之前会先封装 udphdr 

udphdr 数据填充之后, 我们拆解一下关键的信息项, 注意 dump 出来的内存为小端序 

source_port 在 0xffff88007f441024 的位置, 值为 0x9857 = 38999 

dst_port 在 0xffff88007f441026 的位置, 值为 0x1f92 = 8082 

length 在 0xffff88007f441028 的位置, 值为 0x00b = 11 

checksum 在 0xffff88007f441030 的位置, 目前还没有计算, 值为 0x0000 = 0 

(gdb) x/20gx 0xffff88007f441000
0xffff88007f441000:	0xffff88007f441400	0x0000000000000040
0xffff88007f441010:	0x00406b6500400045	0x0100007f00401140
0xffff88007f441020:	0x921f57980100007f	0x0033323100000b00
0xffff88007f441030:	0x0000000000000008	0x0000000400000003
0xffff88007f441040:	0x0000000000000238	0x0000000000400238
0xffff88007f441050:	0x0000000000400238	0x000000000000001c
0xffff88007f441060:	0x000000000000001c	0x0000000000000001
0xffff88007f441070:	0x0000000500000001	0x0000000000000000
0xffff88007f441080:	0x0000000000400000	0x0000000000400000
0xffff88007f441090:	0x0000000000000924	0x0000000000000924

计算了 checksum 之后, 填充到 skb 中 

这里的 checksum 为 0xfe1e, 其具体的值不重要 

(gdb) x/20gx 0xffff88007f441000
0xffff88007f441000:	0xffff88007f441400	0x0000000000000040
0xffff88007f441010:	0x00406b6500400045	0x0100007f00401140
0xffff88007f441020:	0x921f57980100007f	0x003332311efe0b00
0xffff88007f441030:	0x0000000000000008	0x0000000400000003
0xffff88007f441040:	0x0000000000000238	0x0000000000400238
0xffff88007f441050:	0x0000000000400238	0x000000000000001c
0xffff88007f441060:	0x000000000000001c	0x0000000000000001
0xffff88007f441070:	0x0000000500000001	0x0000000000000000
0xffff88007f441080:	0x0000000000400000	0x0000000000400000
0xffff88007f441090:	0x0000000000000924	0x0000000000000924

6. 封装 eth 头的地方

ip 层处理之后, 路由查询, 然后来到了 数据链路层, 封装 source_mac, dst_mac, 网络层标志 等 

ethhdr 数据填充之后, 我们拆解一下关键的信息项, 注意 dump 出来的内存为小端序 

source_mac 在 0xffff88007f441002 的位置, 值为 0x000000000000 

dst_mac 在 0xffff88007f441008 的位置, 值为 0x000000000000  

type 在 0xffff88007f44100e 的位置, 值为 0x000b 为 IPV4

(gdb) x/20gx 0xffff88007f441000
0xffff88007f441000:	0x0000000000000000	0x0008000000000000
0xffff88007f441010:	0x004094681f000045	0x0100007f37d41140
0xffff88007f441020:	0x921f44950100007f	0x003332311efe0b00
0xffff88007f441030:	0x0000000000000008	0x0000000400000003
0xffff88007f441040:	0x0000000000000238	0x0000000000400238
0xffff88007f441050:	0x0000000000400238	0x000000000000001c
0xffff88007f441060:	0x000000000000001c	0x0000000000000001
0xffff88007f441070:	0x0000000500000001	0x0000000000000000
0xffff88007f441080:	0x0000000000400000	0x0000000000400000
0xffff88007f441090:	0x0000000000000924	0x0000000000000924

7. 封装好了 skb 之后内核传递数据给 driver 的地方

这里两种处理方式, 一种是直接将 skb 传递给 drvier, 直接发送 数据包 

另外一种是 进入 qdisc 队列, 根据给定的策略从 队列中刷出数据到 drvier 发送数据包, 多了一层缓冲 提高效率 

不管是上面哪一种方式, 调用 driver 的入口都是 dev_hard_start_xmit, 调用链路如下  

dev_hard_start_xmit - xmit_one - netdev_start_xmit - __netdev_start_xmit - ops->ndo_start_xmit

我这里虚拟机似乎是存在问题? 一直添加到队列, 但是没有 flush 的过程, 呵呵

ops->ndo_start_xmit 就是对应的硬件驱动发送数据包的具体的实现 

本地回环 设备对应的实现是在 drivers/net/loopback.c 中, 发送数据包的实现为 loopback_xmit, 具体实现是直接调用 netif_rx 来添加数据包入队 

后记

当然 具体的实现还有更多的细节, 这里仅仅是一个 case 来梳理了一些 用户数据 到 封包 到 发送到驱动的流程 

参考

  0voice/linux_kernel_wiki 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络(七):DNS协议和原理,DNS为什么用UDP,网页解析的全过程

文章目录一、什么是DNS二、DNS的作用三、DNS作用四、DNS为什么用UDP五、如果打开一个网站很慢&#xff0c;要如何排查六、网页解析的全过程一、什么是DNS DNS是域名系统的英文缩写&#xff0c;是一种组织成域层次结构的计算机和网络服务命名系统&#xff0c;用于TCP/IP网络。 …

数据库行业的 “叛逆者”:大数据已“死”,MotherDuck 当立

“大数据”已死——现今我们最重要的事情不是担心数据大小&#xff0c;而是专注于我们将如何使用它来做出更好的决策。数据库行业发展至今&#xff0c;在数据层面有很多的加速和变革&#xff0c;尤其是过去几年的云数仓爆炸式增长&#xff0c;带来了行业的很多变化。毫无疑问&a…

Fiddler报文分析-断点应用、模拟网络限速-HTTPS的 拦截

目录 一、报文分析 Statistics 请求性能数据 检查器&#xff08;Inspectors&#xff09; 自定义响应&#xff08;AutoResponder&#xff09; Composer Composer的功能就是用来创建HTTP Request然后发送请求。 允许自定义请求发送到服务器&#xff0c;即可以手动创建一个新…

Acwing---1242. 修改数组——并查集的简单应用

修改数组1.题目2.基本思想3.代码实现1.题目 给定一个长度为 N 的数组 A[ A1,A2,⋅⋅⋅AN ] &#xff0c;数组中有可能有重复出现的整数。 现在小明要按以下方法将其修改为没有重复整数的数组。 小明会依次修改 A2,A3,⋅⋅⋅,AN。 当修改 Ai 时&#xff0c;小明会检查 Ai 是…

在VScode里面添加Python解释器

VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器&#xff0c;而想要额外添加新的Python解释器就需要自己设置。 Python和VScode编辑器安装在Windows系统中 Python扩展插件安装在VScode编辑器 第一步&#xff0c;打开VScode编辑…

leaflet 绘制渐变折线(094)

第094个 点击查看专栏目录 本示例的目的是介绍如何在vue+leaflet中绘制渐变折线。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)安装插件相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:htt…

分布式算法 - Raft算法

Paxos是出了名的难懂&#xff0c;而Raft正是为了探索一种更易于理解的一致性算法而产生的。它的首要设计目的就是易于理解&#xff0c;所以在选主的冲突处理等方式上它都选择了非常简单明了的解决方案。推荐阅读提示强烈推荐通过如下资料学习raft。 raft.github.io这里面有一个…

基于Springboot+vue+协同过滤+前后端分离+智能图书推荐系统(用户,多商户,管理员)

技术栈: SpringBoot,SSM, MYSQL, Vue,Layui,JQUERY,HTML,CSS, JAVASCRIPT,前后台分离,日历控件,协同过滤(余弦函数)-基于用户过滤首页访问 http://localhost:8080/vue_gxhfztjxt管理员admin 123456商家 seller1 123456买家 user1 123456代码下载链接&#xff1a;https://pan.ba…

【转载】STM32 Timer定时器开机立即进入中断问题(HAL库)

【转载】STM32 Timer定时器开机立即进入中断问题&#xff08;HAL库&#xff09;问题1问题2F407定时器更新中断问题&#xff08;TIM_IT_Update中断&#xff09;STM32 定时器有时一开启就进中断的话题[STM32CubeF4] HAL中的定时器中断处理函数&#xff0c;存在重复进入的隐患问题…

(二)变形梯度和相对变形梯度

本文主要内容如下&#xff1a;1. 变形梯度2. 变形梯度的逆3. 相对变形梯度4. 两点张量1. 变形梯度 a. 运动变形前&#xff0c;参考构型中某代表性物质点 A 邻域内的线元&#xff1a; dX⃗dXAG⃗Adxic⃗id\vec{X}dX^A\vec{G}_Adx^i\vec{c}_idXdXAGA​dxici​ b. 运动变形后&…

Laravel框架01:composer和Laravel简介

Laravel框架01&#xff1a;composer和Laravel简介一、Composer介绍二、创建Laravel项目三、Laravel目录结构四、Laravel启动方式一、Composer介绍 composer 是PHP中用来管理依赖关系的工具。类似于Javascript的NPM。composer官网&#xff1a;https://getcomposer.org/安装结束…

【bug】Transformer输出张量的值全部相同?!

【bug】Transformer输出张量的值全部相同&#xff1f;&#xff01;现象原因解决现象 输入经过TransformerEncoderLayer之后&#xff0c;基本所有输出都相同了。 核心代码如下&#xff0c; from torch.nn import TransformerEncoderLayer self.trans TransformerEncoderLayer…

SPARC体系下硬浮点编译故障分析

问题说明 之前extension版的app工程都是用的软浮点编译的&#xff0c;在增加姿控算法库后&#xff0c;统一改用硬浮点运行&#xff0c;发现之前一个浮点数解析不对了&#xff0c;排查发现和工程编译选项有关&#xff0c;为软浮点时正常&#xff0c;硬浮点时异常。该问题脱离业…

【华为OD机试模拟题】用 C++ 实现 - VLAN 资源池(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

Stream操作流 练习

基础数据&#xff1a;Data AllArgsConstructor NoArgsConstructor public class User {private String name;private int age;private String sex;private String city;private Integer money; static List<User> users new ArrayList<>();public static void m…

【计算机三级网络技术】 第一篇 网络系统结构与系统设计的基本原则

网络系统结构与系统设计的基本原则 文章目录网络系统结构与系统设计的基本原则一、计算机网络的基本结构二、计算机网络分类及其互联方式1.局域网2.城域网3.广域网4.计算机网络的互联方式三、局域网技术四、城域网技术1.城域网的概念2.宽带城域网建设产生的影响3.推动城域网快速…

HTML - 扫盲

文章目录1. 前言2. HTML2.1 下载 vscode3 HTML 常见标签3.1 注释标签3.2 标题标签3.3 段落标签3.4 换行标签3.5 格式化标签1. 加粗2. 倾斜3. 下划线3.6 图片标签3.7 超链接标签3.8 表格标签3.9 列表标签4. 表单标签4.1 from 标签4.2 input 标签4.3 select 标签4.4 textarea标签…

webgl渲染优化——深度缓冲区、多边形缓冲机制

文章目录前言深度缓冲区多边形缓冲机制总结前言 webgl在渲染三维场景时&#xff0c;按照Z坐标的值决定前后关系&#xff0c;但是在默认状态下它并未开启深度检测&#xff0c;而是将后绘制的物体放在前面&#xff1b;当两个物体Z坐标相差无几时&#xff0c;会产生深度冲突&…

【Redis】线程模型:Redis是单线程还是多线程?

【Redis】线程模型&#xff1a;Redis是单线程还是多线程&#xff1f; 文章目录【Redis】线程模型&#xff1a;Redis是单线程还是多线程&#xff1f;Redis 是单线程吗&#xff1f;Redis 单线程模式是怎样的&#xff1f;Redis 采用单线程为什么还这么快&#xff1f;Redis 6.0 之前…

高端装备的AC主轴头结构

加工机器人的AC主轴头和位置相关动力学特性1. 位置依赖动态特性及其复杂性2. AC主轴头2.1 常见主轴头摆角结构2.2 摆动机构3. 加装AC主轴头的作用和局限性4. 切削机器人的减速器类型5. 其他并联结构形式参考文献资料1. 位置依赖动态特性及其复杂性 However, FRF measurements …