linux下UDP组播接收不到数据的说明

编程入门 行业动态 更新时间:2024-10-08 12:32:14

linux下UDP<a href=https://www.elefans.com/category/jswz/34/1741126.html style=组播接收不到数据的说明"/>

linux下UDP组播接收不到数据的说明

背景

在一个跨平台的桌面项目中,由于涉及多线程中对象的创建、销毁等,基于QT的对象绑定机制(QObject子类)来做实现时,需要相当心累的设计,才能避免跨线程的异常。由于QT的这个天然机制,在实现很多业务(非界面)模块时,都避免了基于QObject。网络模块中的UDP等功能,同样的也未基于QUdpSocket及其相关,而是包装了libuv(尽管,对桌面来说libuv也需要再折腾,但libuv作者已经明示了这个点了)

 

现象

基于libuv的程序示例,在windows下,可以正常工作(接收到组播数据),但是,同样的代码,在linux下无法接收到组播数据。

大概的实现流程是:按照libuv的官方说明(初始化sockaddr结构、udp操作),使用uv_ip4_addr、uv_udp_init、uv_udp_bind、uv_udp_set_membership等接口,很快就实现了一个可接收组播的原型,大概如下:

uv_udp_t* udpHandle = new uv_udp_t;
sockaddr_in addr;
int iRet = -1;
std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
int localPort = 15909;
iRet = ::uv_ip4_addr(localIP.c_str(), localPort, &addr);
iRet = ::uv_udp_init(objLoop, udpHandle);
iRet = ::uv_udp_bind(udpHandle, (const sockaddr*)&addr, UV_UDP_REUSEADDR);
iRet = ::uv_udp_set_membership(udpHandle, multicastIP.c_str(),localIP.c_str(), UV_JOIN_GROUP);

分析

由于linux组播实操经历的缺失,先期主要是搜看各类文章:有些文章,直接采用了修改路由等各种配置解决;有些文章,也采用了linux下绑定0.0.0.0即any的方式来接所有;另外一些,也说明了linux下与windows中组播的区别(但是,并没有适当的关键字以方便大家的搜索)。

最终,还是又继续翻看了UNIX网络编程(中文 第3版)的第21章。其中直接有关的是:

21.9节开始的模糊不清的文字说明,以及图21-14的代码示例(2、12-17行),还有sap.mcast对应着(224.2.127.254);

21.10节,这段话“我们想要给接收套接字捆绑多播组和端口,譬如说239.255.1.2端口8888。(回顾一下,我们可以只捆绑通配IP地址和端口8888,不过还是捆绑多播地址以防止目的端口同为8888的其他数据报到达本套接字)”(无论允许与否,我都想吐槽一下的:这种不加标点的长句子,是在锻炼国码奴的堆栈能力吧)。下面是英文版书中的对应文字(反倒是直观了):

We want the receiving socket to bind the multicast group and port, say 239.255.1.2 port 8888. (Recall that we could just bind the wildcard IP address and port 8888, but binding the multicast address prevents the socket from receiving any other datagrams that might arrive destined for port 8888.)

 

与本问题,间接有关的是,第21.2、21.3小节的原理性说明。

由以上,在linux下接收组播数据时,终于可以舒适安心的采用先绑定组播IP的方式进行实现了。

 

方案

1、基于libuv的方案示例

uv_udp_t* udpHandle = new uv_udp_t;
sockaddr_in addr;
int iRet = -1;
std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
int localPort = 15909;#if defined(_WIN32)
iRet = ::uv_ip4_addr(localIP.c_str(), localPort, &addr);
#else
// 下面是linux下,与windows的不同点
iRet = ::uv_ip4_addr(multicastIP.c_str(), localPort, &addr);	
#endif// 以下是通用的
iRet = ::uv_udp_init(libuvLoop, udpHandle);
iRet = ::uv_udp_bind(udpHandle, (const sockaddr*)&addr, UV_UDP_REUSEADDR);
iRet = ::uv_udp_set_membership(udpHandle, multicastIP.c_str(),localIP.c_str(), UV_JOIN_GROUP);

2、使用原生实现的方案示例

可能你在项目中,并不使用libuv,因此,本文也给出一个大概的示例

int udpSocket = socket(AF_INET, SOCK_DGRAM, 0);sockaddr_in addr;
int iRet = -1;
std::string localIP = "192.168.2.102";	// 本机的某个网卡的IP
std::string multicastIP = "224.1.1.8";	// 加入的组播IP,以接收数据
int localPort = 15909;addr.sin_family = AF_INET;
addr.sin_port = htons(localRecvPort);				// 本地想接收的端口#if defined(_WIN32)
addr.sin_addr.s_addr = inet_addr(localIP.c_str());	// 本机的某个网卡的IP
#else
// 下面是linux下,与windows的不同点
// linux加入组播时,本机需绑定0.0.0.0或者组播地址
addr.sin_addr.s_addr = inet_addr(multicastIP.c_str());	
#endifbind(udpSocket, (sockaddr *)&saddr, sizeof(addr));
int reuseOn = 1;
setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
ip_mreq mreq;  
mreq.imr_multiaddr.s_addr = inet_addr(multicastIP.c_str());  
mreq.imr_interface.s_addr = inet_addr(localIP.c_str());  
setsockopt(udpSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

 

相关文章

.html

更多推荐

linux下UDP组播接收不到数据的说明

本文发布于:2024-03-23 20:44:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1742616.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:组播   数据   linux   UDP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!