在同一端口上接收多个多播源 - C、Linux
我有一个应用程序正在同一端口上从多个多播源接收数据。我能够接收数据。然而,我试图解释每组的统计数据(即收到的消息、收到的字节),并且所有数据都变得混乱。有谁知道如何解决这个问题?如果我尝试查看发送者的地址,它不是多播地址,而是发送机器的 IP。
我正在使用以下套接字选项:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
以及:
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
I have an application that is receiving data from multiple multicast sources on the same port. I am able to receive the data. However, I am trying to account for statistics of each group (i.e. msgs received, bytes received) and all the data is getting mixed up. Does anyone know how to solved this problem? If I try to look at the sender's address, it is not the multicast address, but rather the IP of the sending machine.
I am using the following socket options:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3");
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
and also:
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
经过几年面对这种linux奇怪的行为,并使用前面答案中描述的绑定解决方法,我意识到ip(7) 手册页 描述了一个可能的解决方案:
然后,您可以使用以下命令激活过滤器以接收已加入组的消息:
此问题以及启用 IP_MULTICAST_ALL 的解决方法在 Redhat Bug 231899,此讨论包含重现该问题并解决该问题的测试程序。
After some years facing this linux strange behaviour, and using the bind workaround describe in previous answers, I realize that the ip(7) manpage describe a possible solution :
Then you can activate the filter to receive messages of joined groups using :
This problem and the way to solve it enabling IP_MULTICAST_ALL is discussed in Redhat Bug 231899, this discussion contains test programs to reproduce the problem and to solve it.
[编辑以澄清
bind()
实际上可能包含多播地址。]因此,应用程序正在加入多个多播组,并接收发送到其中任何一个的消息到同一端口。
SO_REUSEPORT
允许您将多个套接字绑定到同一端口。除了端口之外,bind()
还需要一个 IP 地址。INADDR_ANY
是一种包罗万象的地址,但也可以使用 IP 地址,包括多播地址。在这种情况下,只有发送到该 IP 的数据包才会传递到套接字。即,您可以创建多个套接字,每个多播组一个。bind()
每个套接字到 (group_addr, port),并加入 group_addr。然后,发送给不同组的数据将显示在不同的套接字上,您将能够通过这种方式进行区分。我测试了以下方法在 FreeBSD 上的工作原理:
如果您针对不同的多播地址运行多个此类进程,并向其中一个地址发送消息,则只有相关进程才会收到该消息。当然,在您的情况下,您可能希望在一个进程中拥有所有套接字,并且您必须使用
select
或poll
或等效方法来读取所有套接字。[Edited to clarify that
bind()
may in fact include a multicast address.]So the application is joining several multicast groups, and receiving messages sent to any of them, to the same port.
SO_REUSEPORT
allows you to bind several sockets to the same port. Besides the port,bind()
needs an IP address.INADDR_ANY
is a catch-all address, but an IP address may also be used, including a multicast one. In that case, only packets sent to that IP will be delivered to the socket. I.e. you can create several sockets, one for each multicast group.bind()
each socket to the (group_addr, port), AND join group_addr. Then data addressed to different groups will show up on different sockets, and you'll be able to distinguish it that way.I tested that the following works on FreeBSD:
If you run several such processes, for different multicast addresses, and send a message to one of the addresses, only the relevant process will receive it. Of course, in your case, you probably will want to have all the sockets in one process, and you'll have to use
select
orpoll
or equivalent to read them all.根据您的平台(假定为 IPv4)使用
setsockopt()
和IP_PKTINFO
或IP_RECVDSTADDR
。与recvmsg()
或WSARecvMsg()
结合使用,您可以找到每个数据包的源和目标地址。Unix/Linux,请注意 FreeBSD 使用
IP_RECVDSTADDR
,同时两者都支持 IPv6 的IP6_PKTINFO
。Windows,还有
IP_ORIGINAL_ARRIVAL_IF
Use
setsockopt()
andIP_PKTINFO
orIP_RECVDSTADDR
depending on your platform, assuming IPv4. This combined withrecvmsg()
orWSARecvMsg()
allows you to find the source and destination address of every packet.Unix/Linux, note FreeBSD uses
IP_RECVDSTADDR
whilst both supportIP6_PKTINFO
for IPv6.Windows, also has
IP_ORIGINAL_ARRIVAL_IF
将
mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
替换为
mc_addr.sin_addr.s_addr = inet_addr (mc_addr_str);
这对我(linux)有帮助,对于我的每个应用程序在一个端口上接收来自单独多播组的单独多播流。
您还可以查看 VLC 播放器源代码,它在一个端口上显示来自不同 mcast 组的许多 mcast iptv 频道,但我不知道它如何分隔频道。
Replace
mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);
with
mc_addr.sin_addr.s_addr = inet_addr (mc_addr_str);
it's help for me (linux), for each application i receive separate mcast stream from separate mcast group on one port.
Also you can look into VLC player source, it show many mcast iptv channel from different mcast group on one port, but i dont know, how it separetes channel.
我必须使用多个套接字,每个套接字查看不同的多播组地址,然后单独计算每个套接字的统计信息。
如果有办法查看上面答案中提到的“收件人地址”,我无法弄清楚。
一个重要的一点也花了我一段时间——当我像大多数 python 示例一样将每个单独的套接字绑定到一个空白地址时:
我在每个套接字上获取了所有多播数据包(来自所有多播组),但这没有帮助。为了解决这个问题,我将每个套接字绑定到它自己的多播组
然后它就起作用了。
I have had to use multiple sockets each looking at different multicast group addresses, and then count statistics on each socket individually.
If there is a way to see the "receiver's address" as mentioned in the answer above, I can't figure it out.
One important point that also took me awhile - when I bound each of my individual sockets to a blank address like most python examples do:
I got all the multicast packets (from all multicast groups) on each socket, which didn't help. To fix this, I bound each socket to it's own multicast group
And it then worked.
IIRC recvfrom() 为每个发送者提供不同的读取地址/端口。
您还可以在每个数据包中放置一个标头来标识源发送者。
IIRC recvfrom() gives you a different read address/port for each sender.
You can also put a header in each packet identifying the source sender.
多播地址将是数据包中接收者的地址而不是发送者的地址。查看接收者的 IP 地址。
The Multicast address will be the receiver's address not sender's address in the packet. Look at the receiver's IP address.
您可以通过查看接收到的数据包的目标 IP 地址(始终是多播地址)来分离多播流。执行此操作有些复杂:
绑定到
INADDR_ANY
并设置IP_PKTINFO
套接字选项。然后,您必须使用recvmsg()
接收多播 UDP 数据包并扫描IP_PKTINFO
控制消息。这将为您提供接收到的 UDP 数据包的一些边带信息:查看 ipi_addr:这将是您刚刚收到的 UDP 数据包的多播地址。您现在可以处理特定于您正在接收的每个多播流(多播地址)的接收数据包。
You can separate the multicast streams by looking at the destination IP addresses of the received packets (which will always be the multicast addresses). It is somewhat involved to do this:
Bind to
INADDR_ANY
and set theIP_PKTINFO
socket option. You then have to userecvmsg()
to receive your multicast UDP packets and to scan for theIP_PKTINFO
control message. This gives you some side band information of the received UDP packet:Look at ipi_addr: This will be the multicast address of the UDP packet you just received. You can now handle the received packets specific for each multicast stream (multicast address) you are receiving.