Was bedeutet es, einen Multicast-Socket (UDP) zu binden?

71

Ich verwende Multicast-UDP zwischen Hosts mit mehreren Netzwerkschnittstellen. Ich verwende boost :: asio und bin verwirrt über die 2 Operationen, die Empfänger ausführen müssen: Binden, dann Join-Gruppe.

Warum müssen Sie während der Bindung die lokale Adresse einer Schnittstelle angeben, wenn Sie dies mit jeder Multicast-Gruppe tun, der Sie beitreten?

Die Schwesterfrage betrifft den Multicast-Port: Da Sie während des Sendens an eine Multicast-Adresse und einen Multicast-Port senden, geben Sie beim Abonnieren einer Multicast-Gruppe nur die Adresse an, nicht den Port - den Port, der im verwirrenden Aufruf an angegeben wird binden.

Hinweis: Die "Join-Gruppe" ist ein Wrapper-Over setsockopt(IP_ADD_MEMBERSHIP), der, wie dokumentiert, mehrmals am selben Socket aufgerufen werden kann, um verschiedene Gruppen (über verschiedene Netzwerke?) Zu abonnieren. Es wäre daher absolut sinnvoll, den Bindungsaufruf zu beenden und den Port jedes Mal anzugeben, wenn ich eine Gruppe abonniere.

Soweit ich weiß, funktioniert es sehr gut, immer an "0.0.0.0" zu binden und die Schnittstellenadresse beim Beitritt zur Gruppe anzugeben. Verwirrt.

Haelix
quelle

Antworten:

61

Das Binden eines UDP-Sockets beim Empfang von Multicast bedeutet, eine Adresse und einen Port anzugeben, von denen Daten empfangen werden sollen (KEINE lokale Schnittstelle, wie dies bei der TCP-Akzeptorbindung der Fall ist). Die in diesem Fall angegebene Adresse hat eine Filterfunktion , dh der Socket empfängt nur Datagramme, die an diese Multicast-Adresse und diesen Port gesendet werden, unabhängig davon, welche Gruppen anschließend vom Socket verbunden werden. Dies erklärt, warum ich beim Binden an INADDR_ANY (0.0.0.0) Datagramme erhalten habe, die an meine Multicast-Gruppe gesendet wurden, während ich beim Binden an eine der lokalen Schnittstellen nichts erhalten habe, obwohl die Datagramme in dem Netzwerk gesendet wurden, an das diese Schnittstelle gesendet wurde korrespondierte.

Zitat aus UNIX® Network Programming Volume 1, Third Edition: Die Sockets Networking API von WR Stevens. 21.10. Senden und Empfangen

[...] Wir möchten, dass der empfangende Socket die Multicast-Gruppe und den Port bindet, z. B. 239.255.1.2, Port 8888. (Denken Sie daran, dass wir nur die Platzhalter-IP-Adresse und den Port 8888 binden könnten, aber das Binden der Multicast-Adresse verhindert, dass der Socket empfängt Alle anderen Datagramme, die möglicherweise für Port 8888 ankommen.) Wir möchten dann, dass der empfangende Socket der Multicast-Gruppe beitritt. Der sendende Socket sendet Datagramme an dieselbe Multicast-Adresse und denselben Port, z. B. 239.255.1.2, Port 8888.

Haelix
quelle
Führen Sie dieses Experiment durch: Erstellen Sie in derselben App zwei Sockets und schließen Sie sie jeweils einer anderen Gruppe an. Senden Sie Datenverkehr an beide Gruppen (mit identischen Portnummern!) - Wenn Sie die Adresse beim Binden nicht festlegen, erhalten Sie Datenverkehr für beide Gruppen, glaube ich ...
nhed
1
@nhed: Unter Linux muss es nicht einmal im selben Prozess sein. Wenn Sie an 0.0.0.0 binden, erhalten Sie den gesamten Multicast-Verkehr zu dem Port, für den Sie und andere Prozesse auf dem Host eine Gruppenmitgliedschaft hinzugefügt haben.
Johannes Overmann
@ JohannesOvermann, stimmte zu. Ich habe nur einen einfachen Test vorgeschlagen, um zu zeigen, dass die OP-Behauptung always binding to "0.0.0.0" and specifying the interface address when joining the group, works very wellfalsch ist
19:31 Uhr
Warum ist es falsch? Hinweis: Ich war nicht sehr explizit in dem, was ich unter "funktioniert sehr gut" verstehe. Was ich damit meinte war, dass das Binden an "0.0.0.0" mir half, Verkehr zu empfangen, im Gegensatz zum Binden an die lokale Schnittstellen-IP für das Netzwerk, über das Verkehr gesendet wurde, was nicht half. Die akzeptierte Antwort erklärt, warum dies der Fall war.
Haelix
52

Die "Bind" -Operation lautet im Wesentlichen: "Verwenden Sie diesen lokalen UDP-Port zum Senden und Empfangen von Daten. Mit anderen Worten, dieser UDP-Port wird ausschließlich für Ihre Anwendung verwendet. (Gleiches gilt für TCP-Sockets.)

Wenn Sie an "0.0.0.0" ( INADDR_ANY) binden , weisen Sie die TCP / IP-Schicht grundsätzlich an, alle verfügbaren Adapter zum Abhören zu verwenden und den besten Adapter zum Senden auszuwählen. Dies ist Standard für die meisten Socket-Codes. Das einzige Mal, dass Sie 0 für die IP-Adresse nicht angeben würden, ist, wenn Sie auf einem bestimmten Netzwerkadapter senden / empfangen möchten.

Wenn Sie während der Bindung einen Portwert von 0 angeben, weist das Betriebssystem diesem Socket eine zufällig verfügbare Portnummer zu. Ich würde also erwarten, dass Sie für UDP-Multicast an einer bestimmten Portnummer, an die Multicast-Verkehr gesendet werden soll, an INADDR_ANY binden.

Die Operation "Join Multicast Group" ( IP_ADD_MEMBERSHIP) wird benötigt, da Ihr Netzwerkadapter grundsätzlich nicht nur auf Ethernet-Frames wartet, bei denen die Ziel-MAC-Adresse Ihre eigene ist, sondern auch darauf, dass der Ethernet-Adapter ( NIC ) auf IP-Multicast-Verkehr wartet gut für die entsprechende Multicast-Ethernet-Adresse. Jede Multicast-IP wird einer Multicast-Ethernet-Adresse zugeordnet. Wenn Sie einen Socket zum Senden an eine bestimmte Multicast-IP verwenden, wird die Ziel-MAC-Adresse im Ethernet-Frame auf die entsprechende Multicast-MAC-Adresse für die Multicast-IP festgelegt. Wenn Sie einer Multicast-Gruppe beitreten, konfigurieren Sie die Netzwerkkarte so, dass sie auf Datenverkehr wartet, der an dieselbe MAC-Adresse gesendet wird (zusätzlich zu ihrer eigenen).

Ohne die Hardwareunterstützung wäre Multicast nicht effizienter als reine Broadcast-IP-Nachrichten. Der Join-Vorgang weist Ihren Router / Ihr Gateway außerdem an, Multicast-Verkehr von anderen Netzwerken weiterzuleiten. (Erinnert sich jemand an MBONE?)

Wenn Sie einer Multicast-Gruppe beitreten, wird der gesamte Multicast-Verkehr für alle Ports an dieser IP-Adresse von der Netzwerkkarte empfangen. Nur der für Ihren gebundenen Überwachungsport bestimmte Datenverkehr wird über den TCP / IP-Stapel an Ihre App weitergeleitet. In Bezug darauf, warum Ports während eines Multicast-Abonnements angegeben werden - weil Multicast-IP genau das ist - nur IP. "Ports" sind eine Eigenschaft der oberen Protokolle (UDP und TCP).

Sie können mehr darüber lesen, wie Multicast-IP-Adressen Multicast-Ethernet-Adressen an verschiedenen Standorten zugeordnet werden. Der Wikipedia-Artikel ist so gut wie es nur geht:

Die IANA besitzt die OUI-MAC-Adresse 01: 00: 5e, daher werden Multicast-Pakete unter Verwendung des Ethernet-MAC-Adressbereichs 01: 00: 5e: 00: 00: 00 - 01: 00: 5e: 7f: ff: ff zugestellt. Dies sind 23 Bit des verfügbaren Adressraums. Das erste Oktett (01) enthält das Broadcast / Multicast-Bit. Die unteren 23 Bits der 28-Bit-Multicast-IP-Adresse werden auf die 23 Bits des verfügbaren Ethernet-Adressraums abgebildet.

Selbie
quelle
1
Danke für dein Interesse, Selbie. Die Verwendung von bind (0.0.0.0) ist also relevant, da sie die Schnittstelle angibt, auf der eingehende Unicast- UDP-Pakete empfangen werden sollen . Ich habe beim Binden andere Einstellungen als 0.0.0.0 ausprobiert. Es funktioniert nichts (nicht einmal die spezifische Schnittstelle zum Netzwerk, über die Multicast-Verkehr gesendet wird). Ich verstehe die Bedeutung der bind () -Adresse immer noch nicht.
Haelix
Kleine Anmerkung, für TCP ist es sehr klar, was bind(interfAddr, port)tut. Es werden nur Verbindungen von diesem bestimmten Netzwerk akzeptiert. Ich habe überprüft, ob dies korrekt ist. Bei UDP-Sockets scheint die Bindungsadresse mit dem zweiten Argument von IP_ADD_MEMBERSHIP redundant zu sein. Nicht gerade redundant, da die Einstellung nicht zu funktionieren scheint - nur 0.0.0.0 funktioniert.
Haelix
Ich bin mir nicht sicher, was du mit "nichts funktioniert" meinst. Veröffentlichen Sie einen Code, der das Problem demonstriert. Was das Redundanzproblem betrifft, können Sie wahrscheinlich an alle Adapter (INADDR_ANY == 0.0.0.0) binden und dann eine Multicast-Registrierung auf einer bestimmten Schnittstelle durchführen. Ich vermute, sie wollten nur, dass die Schnittstelle flexibel ist. Lesen Sie in Abschnitt 6.4 dieses Links - tldp.org/HOWTO/Multicast-HOWTO-6.html - eine ähnliche Diskussion darüber, warum die Schnittstelle übergeben werden muss.
Selbie
Mit "nichts funktioniert" meinte ich, dass keine andere Adresse als INADDR_ANY (0.0.0.0) zum Binden des UDP-Sockets geeignet ist (nicht einmal die Adresse der Schnittstelle, über die Multicast-Verkehr auf dem Computer ankommt, dh die als zweites Argument beim Beitritt angegebene die Gruppe). Es gibt keinen Fehler beim Binden. Es ist nur so, dass read()kein Ergebnis erzielt wird (bleibt für immer hängen).
Haelix
1
Dies scheint für Multicast-Empfänger nicht richtig zu sein. Die IP ist hier keine Schnittstelle. Es ist entweder ANY oder die Multicast-Gruppenadresse.
WiSaGaN
10

Korrektur für Was bedeutet es, einen Multicast-Socket (udp) zu binden? solange es teilweise beim folgenden Zitat zutrifft:

Die "Bind" -Operation lautet im Wesentlichen "Verwenden Sie diesen lokalen UDP-Port zum Senden und Empfangen von Daten. Mit anderen Worten, er weist diesen UDP-Port für die ausschließliche Verwendung für Ihre Anwendung zu

Es gibt eine Ausnahme. Wenn die Option angewendet wird, können mehrere Anwendungen denselben Port zum Abhören gemeinsam nutzen (normalerweise hat er einen praktischen Wert für Multicast-Datagramme) SO_REUSEADDR. Zum Beispiel

int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // create UDP socket somehow
...
int set_option_on = 1;
// it is important to do "reuse address" before bind, not after
int res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &set_option_on, 
    sizeof(set_option_on));
res = bind(sock, src_addr, len);

Wenn mehrere Prozesse eine solche "Wiederverwendungsbindung" durchgeführt haben, wird jedes an diesem gemeinsam genutzten Port empfangene UDP-Datagramm an jeden der Prozesse übermittelt (wodurch eine natürliche Verbindung mit Multicast-Verkehr hergestellt wird).

Hier sind weitere Details dazu, was in einigen Fällen passiert:

  1. Der Versuch einer Bindung ("exklusiv" oder "Wiederverwendung") an den freien Port ist erfolgreich

  2. Der Versuch einer "exklusiven Bindung" schlägt fehl, wenn der Port bereits "wiederverwendungsgebunden" ist.

  3. Der Versuch, die Bindung "wiederzuverwenden", schlägt fehl, wenn ein Prozess die "exklusive Bindung" beibehält.

Yury Schkatula
quelle
7

Es ist auch sehr wichtig, einen SENDING-Multicast-Socket von einem RECEIVING-Multicast-Socket zu unterscheiden.

Ich stimme allen obigen Antworten bezüglich des EMPFANGS von Multicast-Sockets zu. Das OP stellte fest, dass das Binden eines RECEIVING-Sockets an eine Schnittstelle nicht hilfreich war. Es ist jedoch erforderlich, einen Multicast-SENDING-Socket an eine Schnittstelle zu binden.

Für einen SENDING-Multicast-Socket auf einem Multi-Homed-Server ist es sehr wichtig, für jede Schnittstelle, an die Sie senden möchten, einen separaten Socket zu erstellen. Für jede Schnittstelle sollte ein gebundener SENDING-Socket erstellt werden.

  // This is a fix for that bug that causes Servers to pop offline/online.
  // Servers will intermittently pop offline/online for 10 seconds or so.
  // The bug only happens if the machine had a DHCP gateway, and the gateway is no longer accessible.
  // After several minutes, the route to the DHCP gateway may timeout, at which
  // point the pingponging stops.
  // You need 3 machines, Client machine, server A, and server B
  // Client has both ethernets connected, and both ethernets receiving CITP pings (machine A pinging to en0, machine B pinging to en1)
  // Now turn off the ping from machine B (en1), but leave the network connected.
  // You will notice that the machine transmitting on the interface with
  // the DHCP gateway will fail sendto() with errno 'No route to host'
  if ( theErr == 0 )
  {
     // inspired by 'ping -b' option in man page:      
     //      -b boundif
     //             Bind the socket to interface boundif for sending.
     struct sockaddr_in bindInterfaceAddr;
     bzero(&bindInterfaceAddr, sizeof(bindInterfaceAddr));
     bindInterfaceAddr.sin_len = sizeof(bindInterfaceAddr);
     bindInterfaceAddr.sin_family = AF_INET;
     bindInterfaceAddr.sin_addr.s_addr = htonl(interfaceipaddr);
     bindInterfaceAddr.sin_port = 0; // Allow the kernel to choose a random port number by passing in 0 for the port.
     theErr = bind(mSendSocketID, (struct sockaddr *)&bindInterfaceAddr, sizeof(bindInterfaceAddr));
     struct sockaddr_in serverAddress;
     int namelen = sizeof(serverAddress);  
     if (getsockname(mSendSocketID, (struct sockaddr *)&serverAddress, (socklen_t *)&namelen) < 0) {
        DLogErr(@"ERROR Publishing service... getsockname err");
     }
     else
     {
        DLog( @"socket %d bind, %@ port %d", mSendSocketID, [NSString stringFromIPAddress:htonl(serverAddress.sin_addr.s_addr)], htons(serverAddress.sin_port) );
     }

Ohne dieses Update wird beim Multicast-Senden zeitweise sendto () errno 'Keine Route zum Host' angezeigt. Wenn jemand Aufschluss darüber geben kann, warum das Trennen eines DHCP-Gateways dazu führt, dass Mac OS X Multicast-SENDING-Sockets verwirrt werden, würde ich es gerne hören.

Keith Knauber
quelle
1
Wunderbare Antwort, löse meine Frage zu Multicast.
Lygstate