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

BSD协议栈:多播

分用多播和广播数据报

与单播相比,多播与广播需要提交数据报给所有匹配的socket,其他的操作其实大同小异,读者可以参考下文的单播。但是,其中的难点就在于数据报的提交与回收。

实现逻辑:迭代遍历pcb,如果pcb不匹配,就使用continue关键字开启下一轮迭代,直到找到匹配的pcb或迭代到NULL。

如果pcb匹配,判断上一次是否匹配到pcb,如果是,那么调用sbappendaddr提交数据报到上一次的接收队列,并唤醒因为接收队列而阻塞的进程,之后使用last缓存指针保存pcb。

如果last指针为NULL,说明这是第一次匹配到pcb,那就用last保存该pcb,并进行一次是否结束迭代的判断。

但是在提交之前,我们需要拷贝一个副本,

sbappendaddr函数执行成功后会返回1,也就是说,提交数据报成功后就会唤醒阻塞进程。如果返回的是0,说明提交失败,这是因为缓冲区已满导致的问题,所以需要释放mbuf链。除此之外,该函数还会释放mbuf链,但是考虑到我们是多播或广播,可能不是最后一次提交,所以我们需要使用m_copy备份,并使用变量n保存地址,然后提交给接收队列。

这部分程序的精髓在于提交“上一次”,而非“这一次”。如果检查到匹配的pcb就提交数据报,那么我们不得不考虑最后一次可能出现的问题:我们会多留下一个数据报。但是使用“上一次”,当程序运行到这里时,表示前面的循环结束,可以判断是最后一次,因此无需备份,直接使用原始数据报即可。

(这段程序综合考虑了内存的回收释放,对不同情况处理得非常好)

	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
	    in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif)) {
		struct socket *last;
		/*
		 * Deliver a multicast or broadcast datagram to *all* sockets
		 * for which the local and remote addresses and ports match
		 * those of the incoming datagram.  This allows more than
		 * one process to receive multi/broadcasts on the same port.
		 * (This really ought to be done for unicast datagrams as
		 * well, but that would cause problems with existing
		 * applications that open both address-specific sockets and
		 * a wildcard socket listening to the same port -- they would
		 * end up receiving duplicates of every unicast datagram.
		 * Those applications open the multiple sockets to overcome an
		 * inadequacy of the UDP socket interface, but for backwards
		 * compatibility we avoid the problem here rather than
		 * fixing the interface.  Maybe 4.5BSD will remedy this?)
		 */

		/*
		 * Construct sockaddr format source address.
		 */
		udp_in.sin_port = uh->uh_sport;
		udp_in.sin_addr = ip->ip_src;
		m->m_len -= sizeof (struct udpiphdr);
		m->m_data += sizeof (struct udpiphdr);
		/*
		 * Locate pcb(s) for datagram.
		 * (Algorithm copied from raw_intr().)
		 */
		last = NULL;
		for (inp = udb.inp_next; inp != &udb; inp = inp->inp_next) {
			if (inp->inp_lport != uh->uh_dport)
				continue;
			if (inp->inp_laddr.s_addr != INADDR_ANY) {
				if (inp->inp_laddr.s_addr !=
				    ip->ip_dst.s_addr)
					continue;
			}
			if (inp->inp_faddr.s_addr != INADDR_ANY) {
				if (inp->inp_faddr.s_addr !=
				    ip->ip_src.s_addr ||
				    inp->inp_fport != uh->uh_sport)
					continue;
			}

			if (last != NULL) {
				struct mbuf *n;

				if ((n = m_copy(m, 0, M_COPYALL)) != NULL) {
					if (sbappendaddr(&last->so_rcv,
						(struct sockaddr *)&udp_in,
						n, (struct mbuf *)0) == 0) {
						m_freem(n);
						udpstat.udps_fullsock++;
					} else
						sorwakeup(last);
				}
			}
			last = inp->inp_socket;
			/*
			 * Don't look for additional matches if this one does
			 * not have either the SO_REUSEPORT or SO_REUSEADDR
			 * socket options set.  This heuristic avoids searching
			 * through all pcbs in the common case of a non-shared
			 * port.  It * assumes that an application will never
			 * clear these options after setting them.
			 */
			if ((last->so_options&(SO_REUSEPORT|SO_REUSEADDR) == 0))
				break;
		}

		if (last == NULL) {
			/*
			 * No matching pcb found; discard datagram.
			 * (No need to send an ICMP Port Unreachable
			 * for a broadcast or multicast datgram.)
			 */
			udpstat.udps_noportbcast++;
			goto bad;
		}
		if (sbappendaddr(&last->so_rcv, (struct sockaddr *)&udp_in,
		     m, (struct mbuf *)0) == 0) {
			udpstat.udps_fullsock++;
			goto bad;
		}
		sorwakeup(last);
		return;
	}

http://www.kler.cn/a/550057.html

相关文章:

  • 机舱卫生和空气质量改善
  • opensuse [Linux] 系统挂在新的机械硬盘
  • STM32创建静态库lib
  • YOLOv8-OBB:利用TensorRT编写Plugin,CUDA编写后处理相关核函数,TensorRT和CUDA代码分析
  • 网络安全配置截图
  • 【机器学习】向量化使得简单线性回归性能提升
  • IDEA使用codeGPT集合deepseek
  • TLS和SSL的区别
  • 《探秘AI绿色计算:降低人工智能硬件能耗的热点技术》
  • Linux(Centos 7.6)命令详解:head
  • pgsql时间分区
  • 尚硅谷爬虫note005
  • 更改笔记的路径之后怎么让图片的markdown连接自动更新
  • 数据结构 树的存储和遍历
  • 【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十三节】
  • 【设计模式】【行为型模式】中介者模式(Mediator)
  • Python 实用技巧:如何使用 Python 进行批量邮件自动化
  • 网络安全PPDR
  • Jenkins 配置 Git Parameter 四
  • 【kafka系列】生产者