[英]Receiving multiple multicast feeds on the same port - 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));
[編輯澄清bind()
實際上可能包含多播地址。]
因此,應用程序正在連接多個多播組,並將發送到其中任何一個的消息接收到同一端口。 SO_REUSEPORT
允許您將多個套接字綁定到同一端口。 除端口外, bind()
需要一個IP地址。 INADDR_ANY
是一個包羅萬象的地址,但也可以使用IP地址,包括多播地址。 在這種情況下,只有發送到該IP的數據包才會被傳送到套接字。 即,您可以創建多個套接字,每個多播組一個。 bind()
每個套接字到(group_addr,port),並加入group_addr。 然后,發往不同組的數據將顯示在不同的套接字上,您將能夠以這種方式區分它。
我測試了以下適用於FreeBSD:
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
const char *group = argv[1];
int s = socket(AF_INET, SOCK_DGRAM, 0);
int reuse = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)) == -1) {
fprintf(stderr, "setsockopt: %d\n", errno);
return 1;
}
/* construct a multicast address structure */
struct sockaddr_in mc_addr;
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(group);
mc_addr.sin_port = htons(19283);
if (bind(s, (struct sockaddr*) &mc_addr, sizeof(mc_addr)) == -1) {
fprintf(stderr, "bind: %d\n", errno);
return 1;
}
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group);
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
char buf[1024];
int n = 0;
while ((n = read(s, buf, 1024)) > 0) {
printf("group %s fd %d len %d: %.*s\n", group, s, n, n, buf);
}
}
如果您針對不同的多播地址運行多個此類進程,並向其中一個地址發送消息,則只有相關進程才會收到該進程。 當然,在您的情況下,您可能希望在一個進程中擁有所有套接字,並且您將必須使用select
或poll
或等效函數來全部讀取它們。
經過幾年面對這個linux奇怪的行為,並使用前面的答案中描述的綁定解決方法,我意識到ip(7)聯機幫助頁描述了一個可能的解決方案:
IP_MULTICAST_ALL(自Linux 2.6.31起)
此選項可用於將組播消息的傳遞策略修改為綁定到通配符INADDR_ANY地址的套接字。 參數是一個布爾整數(默認為1)。 如果設置為1,則套接字將從整個系統上全局加入的所有組接收消息。 否則,它將僅從已在此特定套接字上顯式連接的組(例如,通過IP_ADD_MEMBERSHIP選項)傳遞消息。
然后,您可以使用以下命令激活過濾器以接收已連接組的消息:
int mc_all = 0;
if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all))) < 0) {
perror("setsockopt() failed");
}
在Redhat Bug 231899中討論了這個問題以及解決它的方法來啟用IP_MULTICAST_ALL,這個討論包含重現問題並解決它的測試程序。
假設使用IPv4,請使用setsockopt()
和IP_PKTINFO
或IP_RECVDSTADDR
具體取決於您的平台。 結合recvmsg()
或WSARecvMsg()
,您可以查找每個數據包的源和目標地址。
Unix / Linux,注意FreeBSD使用IP_RECVDSTADDR
,同時支持IPv6的IP6_PKTINFO
。
Windows,也有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)有幫助,對於每個應用程序,我從一個端口上的單獨mcast組接收單獨的mcast流。
你也可以看一下VLC播放器的來源,它在一個端口上顯示來自不同mcast組的許多mcast iptv頻道,但我不知道,它是如何分配頻道的。
我不得不使用多個套接字,每個套接字查看不同的多播組地址,然后單獨計算每個套接字的統計信息。
如果有辦法看到上面答案中提到的“接收者的地址”,我無法弄清楚。
一個重要的觀點也花了我一段時間 - 當我將每個單獨的套接字綁定到像大多數python示例一樣的空白地址時:
sock[i].bind(('', MC_PORT[i])
我收到了每個套接字上的所有組播數據包(來自所有組播組),這沒有幫助。 為了解決這個問題,我將每個套接字綁定到它自己的多播組
sock[i].bind((MC_GROUP[i], MC_PORT[i]))
然后它起作用了。
IIRC recvfrom()為每個發送者提供不同的讀取地址/端口。
您還可以在每個數據包中標識標識源發送方的標頭。
多播地址將是接收方的地址,而不是數據包中發送方的地址。 查看接收方的IP地址。
您可以通過查看收到的數據包的目標IP地址(始終是多播地址)來分離多播流。 這有點涉及到:
綁定到INADDR_ANY
並設置IP_PKTINFO
套接字選項。 然后,您必須使用recvmsg()
接收多播UDP數據包並掃描IP_PKTINFO
控制消息。 這為您提供了收到的UDP數據包的一些邊帶信息:
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
查看ipi_addr:這將是您剛剛收到的UDP數據包的多播地址。 您現在可以處理特定於您正在接收的每個多播流(多播地址)的接收數據包。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.