我在具有多个网络接口的主机之间使用多播UDP。 我正在使用boost :: asio,并且对接收器必须进行的2个操作感到困惑:bind,然后是join-group。
为什么在绑定期间需要指定接口的本地地址,当您对加入的每个组播组执行此操作时?
姐妹问题涉及多播端口:因为在发送过程中,您发送到多播地址& port,为什么,在订阅组播组期间,您只需指定地址,而不是端口 - 在混乱的绑定调用中指定的端口。
注意:“join-group”是setsockopt(IP_ADD_MEMBERSHIP)
上的包装器,如文档所示,可以在同一个套接字上多次调用以订阅不同的组(通过不同的网络?)。因此,每次订阅组时,抛弃绑定调用并指定端口都是完全合理的。
从我看到的,总是绑定到“0.0.0.0”并在加入组时指定接口地址,效果很好。困惑。
答案 0 :(得分:54)
在接收多播时绑定UDP套接字意味着指定从中接收数据的地址和端口(不是本地接口,如TCP接受器绑定的情况)。在这种情况下指定的地址具有过滤角色,即套接字仅接收发送到该多播地址的数据报&端口,无论随后哪个组都被套接字连接。这解释了为什么当绑定到INADDR_ANY(0.0.0.0)时,我收到发送到我的多播组的数据报,而当绑定到任何本地接口时,我没有收到任何内容,即使数据报是在该接口所在的网络上发送的对应。
引自UNIX®网络编程第1卷,第3版:W.R Stevens的套接字网络API。 21.10。发送和接收
[...]我们希望接收套接字绑定多播组和 端口,比如239.255.1.2端口8888.(回想一下,我们可以绑定 通配符IP地址和端口8888,但绑定多播地址 防止套接字接收可能的任何其他数据报 到达目的地为端口8888.)然后我们想要接收套接字 加入组播组。发送套接字将发送数据报 这个相同的多播地址和端口,比如239.255.1.2端口8888。
答案 1 :(得分:46)
“绑定”操作基本上是说“使用此本地UDP端口发送和接收数据。换句话说,它将该UDP端口分配给您的应用程序专用。(对于TCP套接字也是如此)。” / p>
当你绑定到“0.0.0.0”(INADDR_ANY
)时,你基本上告诉TCP / IP层使用所有可用的适配器进行监听,并选择最适合发送的适配器。这是大多数套接字代码的标准做法。您不希望在IP地址上指定0的唯一情况是您希望在特定网络适配器上发送/接收。
同样,如果在bind期间指定端口值为0,则操作系统将为该套接字分配随机可用的端口号。所以我希望UDP多播,你可以在特定的端口号上绑定到INADDR_ANY,在那里预期会发送多播流量。
需要“加入多播组”操作(IP_ADD_MEMBERSHIP
),因为它基本上告诉您的网络适配器不仅要监听目标MAC地址是您自己的以太网帧,它还会告诉以太网适配器({ {3}})监听IP多播流量以及相应的多播以太网地址。每个多播IP映射到多播以太网地址。使用套接字发送到特定的组播IP时,以太网帧上的目标MAC地址将设置为组播IP的相应组播MAC地址。当您加入多播组时,您正在配置NIC以侦听发送到同一MAC地址的流量(除了它自己的)。
如果没有硬件支持,多播将不会比普通广播IP消息更有效。连接操作还告诉您的路由器/网关转发来自其他网络的多播流量。 (有人记得MBONE吗?)
如果加入组播组,则NIC将接收该IP地址上所有端口的所有组播流量。只有发往绑定侦听端口的流量才会通过TCP / IP堆栈传递到您的应用。关于在多播订阅期间指定端口的原因 - 这是因为多播IP就是 - 仅IP。 “ports”是上层协议(UDP和TCP)的属性。
您可以详细了解多播IP地址如何映射到各个站点的多播以太网地址。 NIC和它一样好:
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位可用的以太网地址空间。
答案 2 :(得分:10)
What does it mean to bind a multicast (udp) socket?的更正,只要在以下引用中部分为真:
“绑定”操作基本上是说“使用此本地UDP端口发送和接收数据。换句话说,它为您的应用程序分配用于独占的UDP端口< / em>的
有一个例外。如果应用了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数据报都将被传递到每个进程(提供与多播流量的自然联合)。
以下是有关在少数情况下会发生什么的更多细节:
尝试对自由端口进行任何绑定(“独占”或“重用”)将成功
如果端口已经“重复绑定”,尝试“独占绑定”将失败
尝试“重用绑定”将失败
答案 3 :(得分:5)
将SENDING多播套接字与RECEIVING多播套接字区分开来也非常重要。
我同意上面有关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()错误&#39;没有到主机的路由&#39;。 如果有人能说明为什么拔掉DHCP网关会导致Mac OS X多播SENDING套接字混淆,我很乐意听到它。