BSD协议栈:多播
- 其他
- 2025-09-04 02:21:01

分用多播和广播数据报
与单播相比,多播与广播需要提交数据报给所有匹配的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; }上一篇
QtMSVC编译器报错C1060
下一篇
set的使用(c++)