79

我在具有多个网络接口的主机之间使用多播 UDP。我正在使用 boost::asio,并且对接收器必须进行的 2 个操作感到困惑:绑定,然后加入组。

为什么在绑定过程中需要指定接口的本地地址,当您对加入的每个多播组执行此操作时?

姐妹问题关于多播端口:由于在发送期间,您发送到多播地址和端口,为什么在订阅多播组期间,您只指定地址,而不是端口 - 在混淆调用中指定的端口绑定。

注意:“join-group”是一个 wrapper setsockopt(IP_ADD_MEMBERSHIP),如文档所述,可以在同一个套接字上多次调用以订阅不同的组(通过不同的网络?)。因此,每次订阅组时放弃绑定调用并指定端口是非常有意义的。

在我看来,总是绑定到“0.0.0.0”并在加入组时指定接口地址,效果很好。使困惑。

4

4 回答 4

67

在接收多播时绑定 UDP 套接字意味着指定从其接收数据的地址和端口(不是本地接口,如 TCP 接受器绑定的情况)。在这种情况下指定的地址具有过滤作用,即套接字将只接收发送到该多播地址和端口的数据报,无论该套接字随后加入了哪些组。这解释了为什么当绑定到 INADDR_ANY (0.0.0.0) 时我收到发送到我的多播组的数据报,而当绑定到任何本地接口时我没有收到任何内容,即使数据报是在该接口的网络上发送的对应。

引用自 UNIX® 网络编程第 1 卷,第三版:WR Stevens 的套接字网络 API。21.10。发送和接收

[...] 我们希望接收套接字绑定多播组和端口,例如 239.255.1.2 端口 8888。(回想一下,我们可以只绑定通配符 IP 地址和端口 8888,但绑定多播地址会阻止套接字接收任何其他可能到达端口 8888 的数据报。)然后我们希望接收套接字加入多播组。发送套接字会将数据报发送到相同的多播地址和端口,例如 239.255.1.2 端口 8888。

于 2012-05-24T14:25:24.560 回答
57

“绑定”操作基本上是说,“使用此本地 UDP 端口发送和接收数据。换句话说,它分配该 UDP 端口供您的应用程序独占使用。(对于 TCP 套接字也是如此)。

当您绑定到“0.0.0.0” ( INADDR_ANY) 时,您基本上是在告诉 TCP/IP 层使用所有可用的适配器进行侦听并选择最佳适配器进行发送。这是大多数套接字代码的标准做法。唯一不会为 IP 地址指定 0 的情况是当您想在特定网络适配器上发送/接收时。

同样,如果您在绑定期间指定端口值为 0,则操作系统将为该套接字分配一个随机可用的端口号。所以我希望对于 UDP 多播,您绑定到特定端口号上的 INADDR_ANY,预计多播流量将发送到该端口号。

“加入多播组”操作 ( IP_ADD_MEMBERSHIP) 是必需的,因为它基本上告诉您的网络适配器不仅要侦听目标 MAC 地址是您自己的以太网帧,它还告诉以太网适配器 ( NIC ) 侦听 IP 多播流量以及相应的多播以太网地址。每个多播 IP 映射到一个多播以太网地址。当您使用套接字发送到特定的多播 IP 时,以太网帧上的目标 MAC 地址设置为多播 IP 的相应多播 MAC 地址。当您加入多播组时,您将 NIC 配置为侦听发送到同一 MAC 地址的流量(除了它自己的)。

如果没有硬件支持,多播不会比普通广播 IP 消息更有效。加入操作还告诉您的路由器/网关转发来自其他网络的多播流量。(有人记得MBONE吗?)

如果加入多播组,该 IP 地址上所有端口的所有多播流量都将被 NIC 接收。只有发往您的绑定侦听端口的流量才会通过 TCP/IP 堆栈传递到您的应用程序。关于为什么在多播订阅期间指定端口 - 这是因为多播 IP 就是这样 - 仅限 IP。“端口”是上层协议(UDP 和 TCP)的属性。

您可以阅读有关多播 IP 地址如何映射到各个站点的多播以太网地址的更多信息。 维基百科的文章差不多是这样:

IANA 拥有 OUI MAC 地址 01:00:5e,因此组播数据包使用以太网 MAC 地址范围 01:00:5e:00:00:00 - 01:00:5e:7f:ff:ff 传送。这是 23 位的可用地址空间。第一个八位字节 (01) 包括广播/多播位。28 位多播 IP 地址的低 23 位映射到可用以太网地址空间的 23 位。

于 2012-05-22T04:00:10.563 回答
11

更正绑定多播 (udp) 套接字是什么意思?只要它在以下引用中部分正确:

“绑定”操作基本上是说,“使用这个本地 UDP 端口发送和接收数据。换句话说,它分配那个 UDP 端口供您的应用程序独占使用

有一个例外。如果应用该选项,多个应用程序可以共享同一个端口进行侦听(通常它对多播数据报具有实用价值) 。SO_REUSEADDR例如

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

如果多个进程进行了这种“重用绑定”,那么在该共享端口上接收到的每个 UDP 数据报都将被传递给每个进程(提供与多播流量的自然连接)。

以下是有关在某些情况下发生的情况的更多详细信息:

  1. 任何绑定(“独占”或“重用”)到释放端口的尝试都将成功

  2. 如果端口已经“重用绑定”,则尝试“独占绑定”将失败

  3. 如果某些进程保持“独占绑定”,则尝试“重用绑定”将失败

于 2013-01-30T20:57:10.630 回答
7

区分 SENDING 多播套接字和 RECEIVING 多播套接字也很重要。

我同意上面关于接收多播套接字的所有答案。OP 指出将 RECEIVING 套接字绑定到接口没有帮助。但是,必须将多播 SENDING 套接字绑定到接口。

对于多宿主服务器上的 SENDING 多播套接字,为要发送到的每个接口创建一个单独的套接字非常重要。应该为每个接口创建一个绑定的 SENDING 套接字。

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

如果没有此修复,多播发送将间歇性地得到 sendto() errno 'No route to host'。如果有人能解释为什么拔掉 DHCP 网关会导致 Mac OS X 多播发送套接字混淆,我很想听听。

于 2015-04-08T23:08:40.807 回答