Sockets 绑定多播(UDP)套接字意味着什么?
我在具有多个网络接口的主机之间使用多播UDP。 我使用的是boost::asio,我对接收器必须执行的两个操作感到困惑:绑定,然后加入组 在绑定期间,当您对加入的每个多播组进行绑定时,为什么需要指定接口的本地地址 姐妹问题涉及多播端口:因为在发送期间,您发送到多播地址和端口,为什么在订阅多播组期间,您只指定地址,而不指定端口-在绑定调用中指定的端口 注意:“加入组”是Sockets 绑定多播(UDP)套接字意味着什么?,sockets,udp,bind,boost-asio,multicast,Sockets,Udp,Bind,Boost Asio,Multicast,我在具有多个网络接口的主机之间使用多播UDP。 我使用的是boost::asio,我对接收器必须执行的两个操作感到困惑:绑定,然后加入组 在绑定期间,当您对加入的每个多播组进行绑定时,为什么需要指定接口的本地地址 姐妹问题涉及多播端口:因为在发送期间,您发送到多播地址和端口,为什么在订阅多播组期间,您只指定地址,而不指定端口-在绑定调用中指定的端口 注意:“加入组”是setsockopt(IP\u ADD\u MEMBERSHIP)上的包装器,如文档所述,可以在同一套接字上多次调用它以订阅不同的
setsockopt(IP\u ADD\u MEMBERSHIP)
上的包装器,如文档所述,可以在同一套接字上多次调用它以订阅不同的组(通过不同的网络?)。因此,放弃bind调用并在每次订阅组时指定端口是非常有意义的
在我看来,加入组时总是绑定到“0.0.0.0”并指定接口地址,效果非常好。困惑。绑定操作基本上是说,“使用此本地UDP端口发送和接收数据。换句话说,它为应用程序分配专用UDP端口。(TCP套接字也是如此)
当您绑定到“0.0.0.0”()时,您基本上是告诉TCP/IP层使用所有可用的适配器进行侦听,并选择最佳的适配器进行发送。这是大多数套接字代码的标准做法。您唯一不为IP地址指定0的时间是在您希望在特定网络适配器上发送/接收时
类似地,如果您在绑定期间指定端口值0,操作系统将为该套接字分配一个随机可用的端口号。因此,对于UDP多播,您将绑定到预期发送多播流量的特定端口号上的INADDR_ANY
需要“加入多播组”操作(IP\u ADD\u MEMBERSHIP
),因为它基本上告诉您的网络适配器不仅要侦听目标MAC地址为您自己的以太网帧,还告诉以太网适配器()侦听IP多播通信以及相应的多播以太网地址。每个多播IP映射到一个多播以太网地址。当您使用套接字发送到特定的多播IP时,以太网帧上的目标MAC地址设置为多播IP的相应多播MAC地址。当您加入在多播组中,您正在配置NIC以侦听发送到同一MAC地址的通信量(除了自己的MAC地址之外)
如果没有硬件支持,多播将不会比普通的广播IP消息更有效。加入操作还告诉路由器/网关转发来自其他网络的多播流量。(有人记得MBONE吗?)
如果您加入一个多播组,NIC将接收该IP地址上所有端口的所有多播流量。只有绑定到您的侦听端口的流量将通过TCP/IP堆栈传递到您的应用程序。关于在多播订阅期间指定端口的原因,这是因为多播IP只是-仅限IPy、 “端口”是上层协议(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位
接收多播时绑定UDP套接字意味着指定从中接收数据的地址和端口(而不是本地接口,如TCP接受器绑定)。本例中指定的地址具有过滤作用,即套接字仅接收发送到该多播地址和端口的数据报,而不管套接字随后加入了哪些组。这解释了为什么绑定到INADDR_ANY(0.0.0.0)时我收到了发送到我的多播组的数据报,而当绑定到任何本地接口时,我没有收到任何东西,即使数据报是在该接口对应的网络上发送的 引自UNIX®网络编程第1卷第三版:W.R Stevens的Sockets Networking API。 21.10.发送和接收 […]我们希望接收套接字绑定多播组和 端口,例如239.255.1.2端口8888。(回想一下,我们可以只绑定 通配符IP地址和端口8888,但绑定多播地址 防止套接字接收任何可能导致错误的其他数据报 到达目的地为8888端口。)然后我们希望接收套接字 加入多播组。发送套接字将向发送数据报 这是相同的多播地址和端口,例如239.255.1.2端口8888 只要以下报价部分正确,则进行更正: “绑定”操作基本上是说,“使用这个本地UDP端口发送和接收数据。换句话说,它将该UDP端口分配给应用程序的独占使用 有一个例外。如果应用了
SO\u 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数据报都将被传递到每个进程(提供与多播通信的自然连接)
以下是有关少数情况下发生的情况的更多详细信息:
// 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) );
}