Empfang mehrerer Multicast-Feeds auf demselben Port – C, Linux

Lesezeit: 7 Minuten

Gigis Benutzeravatar
Gigi

Ich habe eine Anwendung, die Daten von mehreren Multicast-Quellen auf demselben Port empfängt. Ich kann die Daten empfangen. Ich versuche jedoch, Statistiken jeder Gruppe (dh empfangene Nachrichten, empfangene Bytes) zu berücksichtigen, und alle Daten werden durcheinander gebracht. Weiß jemand, wie man dieses Problem löst? Wenn ich versuche, mir die Absenderadresse anzusehen, ist es nicht die Multicast-Adresse, sondern die IP des sendenden Rechners.

Ich verwende die folgenden Socket-Optionen:

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));

und auch:

setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));

Benutzeravatar von mpromonet
mpromonet

Nachdem ich einige Jahre mit diesem seltsamen Linux-Verhalten konfrontiert war und die in früheren Antworten beschriebene Problemumgehung für die Bindung verwendet habe, stelle ich fest, dass die ip(7)-Manpage Beschreiben Sie eine mögliche Lösung:

IP_MULTICAST_ALL (seit Linux 2.6.31)
Diese Option kann verwendet werden, um die Übermittlungsrichtlinie von Multicast-Nachrichten an Sockets zu ändern, die an die Wildcard-INADDR_ANY-Adresse gebunden sind. Das Argument ist eine boolesche Ganzzahl (standardmäßig 1). Wenn der Wert auf 1 gesetzt ist, empfängt der Socket Nachrichten von allen Gruppen, denen global im gesamten System beigetreten wurde. Andernfalls liefert er nur Nachrichten von den Gruppen, denen explizit beigetreten wurde (zum Beispiel über die IP_ADD_MEMBERSHIP-Option) auf diesem speziellen Socket.

Dann können Sie den Filter aktivieren, um Nachrichten von beigetretenen Gruppen zu erhalten, indem Sie:

int mc_all = 0;
if ((setsockopt(sock, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all))) < 0) {
    perror("setsockopt() failed");
}

Dieses Problem und die Möglichkeit, es zu lösen, indem IP_MULTICAST_ALL aktiviert wird, wird in besprochen Redhat-Fehler 231899enthält diese Diskussion Testprogramme, um das Problem zu reproduzieren und zu lösen.

  • Dies ist überhaupt keine Lösung für das beschriebene Problem, da diese Methode nicht verwendet werden kann, um mehrere Multicast-Streams zu trennen, die an denselben Socket gesendet werden (derselbe Port wird in der Frage erwähnt).

    – Johannes Übermann

    20. September 2017 um 22:21 Uhr

  • @JohannesOvermann: Diese Option erlaubt es, auf einem Socket nur die Multicast-Gruppe zu empfangen, die auf diesem Socket abonniert ist, dann beantwortet diese Option die Frage, um zu vermeiden, dass alle Multicast-Gruppen empfangen werden, die von anderen Sockets abonniert werden, die denselben Port teilen (das könnte in einem anderen Prozess sein). , erwähnt die Frage nicht, dass nur ein Socket verwendet wird. Ein Socket pro Multicast-Gruppe lässt diese Option zu, damit der Kernel den entsprechenden Filter erstellt.

    – mpromonet

    20. September 2017 um 22:46 Uhr

  • bei mpromonet: Nein. Die OP sagt ausdrücklich “mehrere Multicast-Quellen auf demselben Port”. Dies bedeutet, dass eine einzelne Steckdose verwendet werden muss. Sie können nicht wirklich mehrere Sockets an denselben Port binden. Wenn Sie mc_all auf 0 setzen, können Sie Multicast-Streams, die an denselben Port gesendet werden, nicht trennen.

    – Johannes Übermann

    21. September 2017 um 16:10 Uhr


  • Dies ist die beste Antwort auf diese Frage, während die Antwort von DS den Job erledigt, ist es viel umständlicher, wenn Sie ändern möchten, welche Multicast-Gruppe ein Socket überwacht, da Sie einen Socket nicht erneut binden können, müssen Sie ihn vollständig schließen die Steckdose und erstellen Sie eine neue.

    – ScottG

    3. Oktober 2019 um 14:02 Uhr

  • Danke schön! Mein Problem ist ähnlich und Ihre Antwort war perfekt. Wenn zwei Programme auf demselben Linux-Rechner dieselbe Portnummer in verschiedenen Multicast-Gruppen abonnieren, sehen Sie Übersprechen. Die Abonnements müssen nicht einmal aus demselben Prozess stammen! Ihre Antwort hat das Übersprechen beendet und jetzt bekomme ich nur das, worum ich gebeten habe.

    – Trade-Ideen Philip

    11. August 2020 um 19:03 Uhr

Benutzeravatar von DS
DS.

[Edited to clarify that bind() may in fact include a multicast address.]

Die Anwendung tritt also mehreren Multicast-Gruppen bei und empfängt Nachrichten, die an eine von ihnen gesendet werden, an demselben Port. SO_REUSEPORT ermöglicht es Ihnen, mehrere Sockets an denselben Port zu binden. Neben dem Hafen, bind() benötigt eine IP-Adresse. INADDR_ANY ist eine Catch-All-Adresse, es kann jedoch auch eine IP-Adresse verwendet werden, einschließlich einer Multicast-Adresse. In diesem Fall werden nur an diese IP gesendete Pakete an den Socket geliefert. Dh Sie können mehrere Sockets erstellen, einen für jede Multicast-Gruppe. bind() jeder Socket zu (group_addr, port), UND tritt group_addr bei. Dann werden Daten, die an verschiedene Gruppen adressiert sind, auf verschiedenen Sockets angezeigt, und Sie können sie auf diese Weise unterscheiden.

Ich habe getestet, dass Folgendes unter FreeBSD funktioniert:

#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);
    }
}

Wenn Sie mehrere solcher Prozesse für verschiedene Multicast-Adressen ausführen und eine Nachricht an eine der Adressen senden, wird nur der relevante Prozess sie erhalten. Natürlich möchten Sie in Ihrem Fall wahrscheinlich alle Sockets in einem Prozess haben, und Sie müssen sie verwenden select oder poll oder gleichwertig, um sie alle zu lesen.

  • Ich glaube nicht, dass Sie an eine Multicast-Adresse binden können – Sie müssen an Ihre lokale Schnittstelle (INADDR_ANY) binden.

    – Giggi

    30. April 2010 um 3:00 Uhr

  • Ich kann nicht unter Linux testen, aber ich habe einen kleinen Test geschrieben (in der bearbeiteten Antwort enthalten) und überprüft, ob er unter FreeBSD korrekt funktioniert.

    – DS.

    2. Mai 2010 um 1:37 Uhr

  • Dies ist die klassische Problemumgehung unter Linux, die seit einiger Zeit verwendet wird. Seit Kernel 2.6.31 ermöglicht eine Socket-Option jedoch das Filtern nach abonnierten Gruppen, siehe Antwort stackoverflow.com/a/20919920/3102264

    – mpromonet

    14. Juni 2014 um 15:53 ​​Uhr

Verwenden setsockopt() Und IP_PKTINFO oder IP_RECVDSTADDR abhängig von Ihrer Plattform, IPv4 vorausgesetzt. Dies kombiniert mit recvmsg() oder WSARecvMsg() ermöglicht es Ihnen, die Quelle zu finden Und Zieladresse jedes Pakets.

Unix/Linux, beachten Sie, dass FreeBSD verwendet wird IP_RECVDSTADDR während beide unterstützen IP6_PKTINFO für IPv6.

Windows hat auch IP_ORIGINAL_ARRIVAL_IF

Benutzeravatar von Helius
Helios

Ersetzen

mc_addr.sin_addr.s_addr = htonl(INADDR_ANY);

mit

mc_addr.sin_addr.s_addr = inet_addr (mc_addr_str);

Es ist eine Hilfe für mich (Linux), für jede Anwendung erhalte ich einen separaten Mcast-Stream von einer separaten Mcast-Gruppe an einem Port.

Sie können auch in die VLC-Player-Quelle schauen, es zeigt viele Mcast-IPTV-Kanäle aus verschiedenen Mcast-Gruppen an einem Port, aber ich weiß nicht, wie es den Kanal trennt.

Ich musste mehrere Sockets verwenden, die jeweils unterschiedliche Multicast-Gruppenadressen betrachten, und dann die Statistiken für jeden Socket einzeln zählen.

Wenn es eine Möglichkeit gibt, die “Adresse des Empfängers” wie in der obigen Antwort erwähnt zu sehen, kann ich es nicht herausfinden.

Ein wichtiger Punkt, der auch eine Weile gedauert hat – als ich jeden meiner einzelnen Sockets an eine leere Adresse gebunden habe, wie es die meisten Python-Beispiele tun:

sock[i].bind(('', MC_PORT[i])

Ich habe alle Multicast-Pakete (von allen Multicast-Gruppen) auf jedem Socket erhalten, was nicht geholfen hat. Um dies zu beheben, habe ich jeden Socket an seine eigene Multicast-Gruppe gebunden

sock[i].bind((MC_GROUP[i], MC_PORT[i]))

Und es hat dann funktioniert.

Benutzeravatar von eile
eile

IIRC recvfrom() gibt Ihnen für jeden Absender eine andere Leseadresse/einen anderen Port.

Sie können auch einen Header in jedes Paket einfügen, der den Absender der Quelle identifiziert.

Benutzeravatar von Himanshu
Himanshu

Die Multicast-Adresse ist die Adresse des Empfängers, nicht die Adresse des Absenders im Paket. Sehen Sie sich die IP-Adresse des Empfängers an.

1443840cookie-checkEmpfang mehrerer Multicast-Feeds auf demselben Port – C, Linux

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy