BSD协议栈:UDP发送

BSD实现

在BSD中UDP头部数据结构如下:

/** Udp protocol header.* Per RFC 768, September, 1981.*/
struct udphdr {u_short	uh_sport;		/* source port */u_short	uh_dport;		/* destination port */short	uh_ulen;		/* udp length */u_short	uh_sum;			/* udp checksum */
};

IP首部实现可以参考lwip,不过笔者简单讲一讲一个相似的结构体struct ipovly。

struct ipovly是在BSD内部使用的一个覆盖IP首部之上的结构,其实并不是真正的IP首部,这是为了方便计算校验和。

/** Overlay for ip header used by other protocols (tcp, udp).*/
struct ipovly {caddr_t	ih_next, ih_prev;	/* for protocol sequence q's */u_char	ih_x1;			/* (unused) */u_char	ih_pr;			/* protocol */short	ih_len;			/* protocol length */struct	in_addr ih_src;		/* source internet address */struct	in_addr ih_dst;		/* destination internet address */
};

IP/UDP首部结构体:

/** UDP kernel structures and variables.*/
struct	udpiphdr {struct 	ipovly ui_i;		/* overlaid ip structure */struct	udphdr ui_u;		/* udp header */
};
#define	ui_next		ui_i.ih_next
#define	ui_prev		ui_i.ih_prev
#define	ui_x1		ui_i.ih_x1
#define	ui_pr		ui_i.ih_pr
#define	ui_len		ui_i.ih_len
#define	ui_src		ui_i.ih_src
#define	ui_dst		ui_i.ih_dst
#define	ui_sport	ui_u.uh_sport
#define	ui_dport	ui_u.uh_dport
#define	ui_ulen		ui_u.uh_ulen
#define	ui_sum		ui_u.uh_sum

UDP发送

UDP发送实现简单介绍如下:

参数

inp:UDP 套接字在内核中的表示,包含了套接字的所有状态和配置信息,例如IP/UDP首部等信息

m:指向输出mbuf链

addr:一个可选指针,指向sockaddr_in结构中的目的地址

control:一个可选指针,指向sendmsg的控制信息

程序执行

1.如果我们没有使用sendmsg这些带控制信息的API,那么就丢弃control参数。

2.指定了目的地址:判断套接字传递过来的目的地址是不是任意地址(0.0.0.0),如果是,那么该地址无意义,返回错误。如果不是,需要通过in_pcbconnect函数临时连接到该目的地址,不过在连接之前需要暂时提升当前程序优先级并保存本地地址。因为临时连接会改变套接字中的外部地址、端口和本地地址, 此时的套接字信息是不完整的。如果此时有 UDP 数据报到达,协议栈在查找匹配的套接字时,可能因地址信息变化而将数据报交给错误的套接字,导致数据被错误的进程接收。

未指定目的地址:判断是不是已经连接上了(例如调用 connect() 函数实现连接),否则也报错。

connect() 函数的本质也是调用in_pcbconnect函数。

3.关于M_PREPEND和mtod可以看mbuf那一节。程序先申请内存,再设置IP/UDP首部,接下来就是计算校验和。

4.检验与计算校验和,在前文笔者简单介绍过校验和的计算,分为初始化、构建、计算、填写四个步骤,读者可以返回前文查看。

初始化与构建:将IP首部覆盖为ui_next,ui->ui_prev,并覆盖其他参数,这两个参数不会影响校验和结果,因为它们被设置为0。现在IP首部被覆盖为:ui_next,ui_prev,ui_x1,ui_pr,ui_len。其中IP首部中的ui_len、ui_src和ui_dst是真正影响校验和结果的参数。计算和填写就不多赘述了,是通过in_cksum实现的。

正如我们前面所说的,UDP的IPv4是允许不进行校验的,只要把校验和填0。而如果计算出来的结果是0,校验和会填写为0xffff。

5.udp_output会填充IP头部的三个字段:服务类型TOS、全长len、生存时间TTL,而ip_output则会填充其他字段。

6.使用ip_output发送数据报。

7.如果socket是临时连接上的,那么调用in_pcbdisconnect断连临时连接的socket,同时恢复本地地址并恢复程序优先级,之后程序结束。

其中in_pcbconnect将会耗费程序近三分之一的时间。

int udp_output(inp, m, addr, control)register struct inpcb *inp;register struct mbuf *m;struct mbuf *addr, *control;
{register struct udpiphdr *ui;register int len = m->m_pkthdr.len;struct in_addr laddr;int s, error = 0;if (control)m_freem(control);		/* XXX */if (addr) {laddr = inp->inp_laddr;if (inp->inp_faddr.s_addr != INADDR_ANY) {error = EISCONN;goto release;}/** Must block input while temporarily connected.*/s = splnet();error = in_pcbconnect(inp, addr);if (error) {splx(s);goto release;}} else {if (inp->inp_faddr.s_addr == INADDR_ANY) {error = ENOTCONN;goto release;}}/** Calculate data length and get a mbuf* for UDP and IP headers.*/M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);if (m == 0) {error = ENOBUFS;goto release;}/** Fill in mbuf with extended UDP header* and addresses and length put into network format.*/ui = mtod(m, struct udpiphdr *);ui->ui_next = ui->ui_prev = 0;ui->ui_x1 = 0;ui->ui_pr = IPPROTO_UDP;ui->ui_len = htons((u_short)len + sizeof (struct udphdr));ui->ui_src = inp->inp_laddr;ui->ui_dst = inp->inp_faddr;ui->ui_sport = inp->inp_lport;ui->ui_dport = inp->inp_fport;ui->ui_ulen = ui->ui_len;/** Stuff checksum and output datagram.*/ui->ui_sum = 0;if (udpcksum) {if ((ui->ui_sum = in_cksum(m, sizeof (struct udpiphdr) + len)) == 0)ui->ui_sum = 0xffff;}((struct ip *)ui)->ip_len = sizeof (struct udpiphdr) + len;((struct ip *)ui)->ip_ttl = inp->inp_ip.ip_ttl;	/* XXX */((struct ip *)ui)->ip_tos = inp->inp_ip.ip_tos;	/* XXX */udpstat.udps_opackets++;error = ip_output(m, inp->inp_options, &inp->inp_route,inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),inp->inp_moptions);if (addr) {in_pcbdisconnect(inp);inp->inp_laddr = laddr;splx(s);}return (error);release:m_freem(m);return (error);
}

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

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

相关文章

QT (四)模型/视图 QFileSystemModel,QStringListModel,QStandardItemModel

思考:QTableWidget 在某种程度上可以等价为QStandardItemModel,同理,其他的功能也有类似的等价,但是以当前的QTableWidget 和QStandardItemModel为例的话,两者都是用于实现建立表格的相关组件,只不过QStand…

Unity热更新方案HybridCLR+YooAsset,从零开始,保姆级教程,纯c#开发热更

文章目录: 一、前言二、创建空工程三、接入HybridCLR四、接入YooAsset五、搭建本地资源服务器Nginx六、实战七、最后八、后记 一、前言 unity热更有很多方案,各种lua热更,ILRuntime等,这里介绍的是YooAssetHybridCLR的热更方案&a…

Zabbix——监控Windows下某个文件夹的所有文件变化

获取所有的指定路径下的文件列表 以D:\Apps这个文件夹下的所有文件为例,我需要找到这个文件夹里面的子文件夹中的所有文件,但是排除backup这个文件夹,下面我们来看怎么操作 在scripts目录下创建check_file_in_D_Apps.ps1文件,内容…

C++ Primer 函数匹配

欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…

java八股文-mysql

1. 索引 1.1 什么是索引 索引(index)是帮助Mysql高效获取数据的数据结构(有序).提高数据的检索效率,降低数据库的IO成本(不需要全表扫描).通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗. 1.2 mysql索引使用的B树? 1. 没有使用二叉树,最坏情况o&…

Docker拉不下来镜像问题解决法案

打开docker的设置界面 配置如下: vi /etc/docker/daemon.json {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": ["…

春招项目=图床+ k8s 控制台(唬人专用)

1. 春招伊始 马上要春招了,一个大气的项目(冲击波项目)直观重要,虽然大家都说基础很重要,但是一个足够新颖的项目完全可以把你的简历添加一个足够闪亮的点。 这就不得不推荐下我的 k8s 图床了,去年折腾快…

wordpress主题插件开发中高频使用的38个函数

核心模板函数 get_header()/get_footer()/get_sidebar() – 加载模板部件 the_title()/the_content()/the_excerpt() – 显示文章标题、内容、摘要 the_post() – 循环中获取文章数据 bloginfo(‘url’) – 获取站点URL wp_head()/wp_footer() – 输出头部/尾部代码 wp_n…

vue点击左边导航,右边出现页面步骤

vue点击左边导航&#xff0c;右边出现页面 步骤 一定要import不然会出错 index.js Course作为Homeview子路由 Homeview加入<Routerview> 点击跳转<RouterLink to> 父Homeview中有RouterView&#xff08;路由出口&#xff0c;跳转至相应路径&#xff09;和Router…

阿里云视频点播,基于thinkphp8上传视频

前端参考官方示例(jQuery版) <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>阿里云 JavaScript上传SDK Demo (使用jquery)</title><script src"__STATIC__/jquery.min.js"></script><sc…

力扣 66.加一 (Java实现)

题目分析 给定一个数组&#xff0c;可以组成一个数字&#xff0c;将数字加一后&#xff0c;返回新数组 思路分析 首先跟着题目思路走&#xff0c;将数组按位*10可以得到数字&#xff0c;再加一&#xff0c;加一后按位%10&#xff0c;可以得到新的数组。但是此处数字会过大&…

[特殊字符] 用Rust重塑Web开发速度极限:Hyperlane框架——开启高性能服务的「光年时代」[特殊字符]

&#x1f525; 每秒百万级请求&#xff1f;Rust超新星Hyperlane框架让Web开发突破性能次元壁&#xff01;&#x1f525; &#x1f31f; 颠覆性技术亮点&#xff1a;为何全球顶尖工程师正疯狂迁移至Hyperlane&#xff1f; ⚡️ 「速度即正义」&#xff1a;重新定义Web性能天花…

DeepSeek与ChatGPT:AI语言模型的全面对决

DeepSeek与ChatGPT&#xff1a;AI语言模型的全面对决 引言&#xff1a;AI 语言模型的时代浪潮一、认识 DeepSeek 与 ChatGPT&#xff08;一&#xff09;DeepSeek&#xff1a;国产新星的崛起&#xff08;二&#xff09;ChatGPT&#xff1a;AI 界的开拓者 二、DeepSeek 与 ChatGP…

visutal studio 2022使用qcustomplot基础教程

编译 下载&#xff0c;2.1.1版支持到Qt6.4 。 拷贝qcustomplot.h和qcustomplot.cpp到项目源目录&#xff08;Qt project&#xff09;。 在msvc中将它俩加入项目中。 使用Qt6.8&#xff0c;需要修改两处代码&#xff1a; L6779 # if QT_VERSION > QT_VERSION_CHECK(5, 2, …

联想笔记本电脑摄像头灯亮,但没有画面怎么解决,

联想小新电脑&#xff0c;遇到电脑黑屏。 解决方法&#xff0c;搜索打开任务管理器 打开联想管家的路径 打开BatterySetting.exe 程序 然后右下角会弹出一个东西&#xff0c;关闭这个摄像头的隐私模式。就可以打开了 就可以了

蓝桥杯单片机大模板(西风)

#include <REGX52.H> #include "Key.h" #include "Seg.h" //变量声明区 unsigned char Key_Val,Key_Down,Key_Old;//按键扫描专用变量 unsigned char Key_Slow_Down;//按键减速专用变量 10ms unsigned int Seg_Slow_Down;//按键扫描专用变量 500ms …

ubuntu服务器 如何配置安全加固措施

下面提供一个更详细、一步步的服务器安全加固指南&#xff0c;适合新手操作。我们将从 Fail2Ban、SSH&#xff08;密钥认证及端口更改&#xff09;、Nginx 速率限制和日志轮转四个方面进行优化&#xff0c;同时补充一些额外的安全建议。 新的服务器&#xff0c;通常我们会创建一…

ESP32通过MQTT连接阿里云平台实现消息发布与订阅

文章目录 前言 一、准备工作 二、阿里云平台配置 三、代码实现 总结 前言 本文将介绍如何使用ESP32开发板通过MQTT协议连接阿里云物联网平台&#xff0c;并实现消息的发布与订阅功能。我们将使用Arduino IDE进行开发&#xff0c;并借助PubSubClient库实现MQTT通信。 一、准备…

Ubuntu下载安装Docker-Desktop

下载 Ubuntu | Docker Docs 预备工作 Ubuntu增加docker apt库-CSDN博客 安装 sudo apt-get updatesudo apt install gnome-terminal# sudo apt install -y docker-composesudo apt-get install ./docker-desktop-amd64.deb 测试 sudo docker run hello-worldHello from D…

Python爬虫实战:获取笔趣阁图书信息,并做数据分析

注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 1. 环境准备与反爬策略 python import requests from bs4 import BeautifulSoup import pandas as pd import re import time import random from fake_useragent …