BSD实现:单播

news/2025/2/21 11:39:19
分用单播数据报

如果程序执行到这里,说明程序并没有执行多播操作,那么大概率是单播。

维护缓存指针

udp_last_inpcb是上一次接收数据报的端口的控制块指针,维护该指针的依据是许多程序往往具有时间局部性,也就是:经常运行的程序下一次往往也还运行,不经常运行的程序下一次大概率不会运行。

迭代全部端口

判断缓存指针指向的是不是现在要找的控制块,如果不是,就调用in_pcblookup函数迭代全部端口。

生成ICMP不可达错误

如果inp为NULL,说明没有找到端口,那么需要生成ICMP错误报告消息。

小结

虽然据TCP/IP卷二中描述,udp_last_inpcb其实没什么用,但笔者看来这里的程序相当巧妙,能从中感受到作者对时间局部性与空间局部性的考虑。inp被复用进行程序流控制,例如inp为NULL时,这说明肯定是进行了寻找操作且没有找到,因为赋值缓存指针必定使inp不为NULL,如果inp匹配缓存指针或者是找到符合的端口,那么生成ICMP错误的代码就直接跳过。

inp这个变量使用register关键字修饰,并被反复使用。可以想象它的值第一次被保存在寄存器后,后面的程序只需要反复操作该寄存器,而不用频繁对其他变量取地址并引用,这对于指令预取和流水线机制都是十分有利的,因为寄存器本身就是速度最快的内存。

(整个UDP协议的代码都写得非常巧妙,精简凝练,对变量的复用不仅能体现全部的控制流,还能体现时间局部性)

	/*
	 * Locate pcb for datagram.
	 */
	inp = udp_last_inpcb;
	if (inp->inp_lport != uh->uh_dport ||
	    inp->inp_fport != uh->uh_sport ||
	    inp->inp_faddr.s_addr != ip->ip_src.s_addr ||
	    inp->inp_laddr.s_addr != ip->ip_dst.s_addr) {
		inp = in_pcblookup(&udb, ip->ip_src, uh->uh_sport,
		    ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD);
		if (inp)
			udp_last_inpcb = inp;
		udpstat.udpps_pcbcachemiss++;
	}
	if (inp == 0) {
		udpstat.udps_noport++;
		if (m->m_flags & (M_BCAST | M_MCAST)) {
			udpstat.udps_noportbcast++;
			goto bad;
		}
		*ip = save_ip;
		ip->ip_len += iphlen;
		icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
		return;
	}
单播提交数据报给对应端口

程序执行到这里都没有返回,说明并不是多播,但肯定是匹配了上一次的控制块或者是找到了控制块,那么是单播。

返回源站IP地址和端口

还记得我们在应用层定义的sockaddr_in变量吗?在这里会把IP地址和端口保存到这个变量中。

返回控制信息

如果定义了控制选项,那么可以使用udp_saveopt分配一个mbuf缓存控制信息(在这里是IP地址)并返回。

提交数据报到socket的接收队列

这一步可以使用sbappendaddr完成,但是在执行之前,需要修正第一个mbuf,忽略UDP首部和IP首部。提交数据报后,需要使用sorwakeup函数唤醒所有阻塞在接收队列的进程。

	/*
	 * Construct sockaddr format source address.
	 * Stuff source address and datagram in user buffer.
	 */
	udp_in.sin_port = uh->uh_sport;
	udp_in.sin_addr = ip->ip_src;
	if (inp->inp_flags & INP_CONTROLOPTS) {
		struct mbuf **mp = &opts;

		if (inp->inp_flags & INP_RECVDSTADDR) {
			*mp = udp_saveopt((caddr_t) &ip->ip_dst,
			    sizeof(struct in_addr), IP_RECVDSTADDR);
			if (*mp)
				mp = &(*mp)->m_next;
		}
下面宏条件编译的代码还没有实现,暂时忽略
#ifdef notyet
		/* options were tossed above */
		if (inp->inp_flags & INP_RECVOPTS) {
			*mp = udp_saveopt((caddr_t) opts_deleted_above,
			    sizeof(struct in_addr), IP_RECVOPTS);
			if (*mp)
				mp = &(*mp)->m_next;
		}
		/* ip_srcroute doesn't do what we want here, need to fix */
		if (inp->inp_flags & INP_RECVRETOPTS) {
			*mp = udp_saveopt((caddr_t) ip_srcroute(),
			    sizeof(struct in_addr), IP_RECVRETOPTS);
			if (*mp)
				mp = &(*mp)->m_next;
		}
#endif
	}
	iphlen += sizeof(struct udphdr);
	m->m_len -= iphlen;
	m->m_pkthdr.len -= iphlen;
	m->m_data += iphlen;
	if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr *)&udp_in,
	    m, opts) == 0) {
		udpstat.udps_fullsock++;
		goto bad;
	}
	sorwakeup(inp->inp_socket);
	return;
bad:
	m_freem(m);
	if (opts)
		m_freem(opts);
}


http://www.niftyadmin.cn/n/5860788.html

相关文章

Nginx:服务架构中不可或缺的基础组件

基本所有的服务架构中,Nginx 都是不可或缺的基础组件: HTTP 负载均衡,将请求转发到后端 API 服务器。健康检查,将后端无法服务的节点移除。配置 HTTPS ,增强安全性。静态服务器,随着前后端分离&#xff0c…

MySQL 视图入门

一、什么是 MySQL 视图 1.1 视图的基本概念 在 MySQL 中,视图是一种虚拟表,它本身并不存储实际的数据,而是基于一个或多个真实表(基表)的查询结果集。可以把视图想象成是一个预定义好的查询语句的快捷方式。当你查询…

玩机日记 12 群晖部署AList并配置SSL,安装opkg,使用rclone挂载到本地

目录 1、部署alist,配置ssl 安装alist 参考官方文档添加网盘 配置ssl 开启webdav访问 2、安装opkg 修改环境变量 添加计划任务 3、安装配置rclone挂载alist 安装rclone,新建remote 安装fuse3 挂载AList 添加计划任务 1、部署alist&#xff0…

【Blender】二、建模篇--05,阵列修改器与晶格形变

阵列修改器是bender里面一个比较常用的修改器,所以我们单独开口来讲,我们会先从几片树叶出发,然后我们用阵列修改器把这几片树叶变成这样的造型和这样的造型。这两个造型分别就代表着阵列修改器最常用的两种偏移方法,我们现在就开始我们先来做几个树叶。 1.树叶建模 首先…

npm、pnpm和yarn有什么区别

1. 性能和速度 npm:在较早的版本中,速度较慢,尤其是在安装大型依赖集时。自npm 5以后的版本引入了缓存机制,性能有所提升。yarn:由Facebook开发,主要目标是提高安装速度。使用了缓存和并行安装(…

Matplotlib 高级图表绘制与交互式可视化(ipywidgets)

目录: ipywidgets 介绍 1. 什么是 ipywidgets 直接开始: 动态调整正弦波频率 随机散点图 启用交互式模式 使用滑块和下拉菜单调整图表样式 使用布局管理器创建复杂界面 使用动画创建动态图表 最后: 综合示例:动态仪表盘 ipywidgets 介绍 1. 什么是 ipywidgets i…

Vue 中组件通信的方式有哪些,如何实现父子组件和非父子组件之间的通信?

一、父子组件通信&#xff08;垂直通信&#xff09; 1. Props 传值&#xff08;父 → 子&#xff09; 实现方案&#xff1a; <!-- Parent.vue --> <template><Child :user"userData" /> </template><script setup> import { ref } …

[kubelet-check] It seems like the kubelet isn‘t running or healthy.

执行k8s时报错&#xff1a; [kubelet-check] It seems like the kubelet isn’t running or healthy. [kubelet-check] The HTTP call equal to ‘curl -sSL http://localhost:10248/healthz’ failed with error: Get "http://localhost:10248/heal ** 解决办法如下&a…