簡體   English   中英

在同一端口上接收多個多播源 - C,Linux

[英]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);
    }
}

如果您針對不同的多播地址運行多個此類進程,並向其中一個地址發送消息,則只有相關進程才會收到該進程。 當然,在您的情況下,您可能希望在一個進程中擁有所有套接字,並且您將必須使用selectpoll或等效函數來全部讀取它們。

經過幾年面對這個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_PKTINFOIP_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM