通过UDP进行IP组播的正确行为是什么?
想象一下自己的情况:
在Windows中使用Winsock完成此操作时,只有在同时满足两个条件的情况下,每个套接字才变为数据:
这是一些Delphi代码:
procedure Connect();
var err: Integer;
wData: WsaData;
reuse: Integer;
begin
FillChar(wData, SizeOf(wData), 0);
err := WSAStartup(MAKEWORD(2, 2), wData);
if err = SOCKET_ERROR then Exit;
_fd := socket(AF_INET, SOCK_DGRAM, 0);
if _fd = INVALID_SOCKET then Exit;
reuse := 1;
if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, Pointer(@reuse), SizeOf(reuse)) < 0) then Exit;
FillChar(_addr, sizeof(_addr), 0);
_addr.sin_family := AF_INET;
if (_listeningInterface = '0.0.0.0') or not _isIpAddress(_listeningInterface) then
_addr.sin_addr.s_addr := htonl(INADDR_ANY)
else
_addr.sin_addr.s_addr := inet_addr(PAnsiChar(_listeningInterface));
_addr.sin_port := htons(_port);
if (bind(_fd, _addr, sizeof(_addr)) < 0) then Exit;
if _isMulticast() then begin
_mreq.imr_multiaddr.s_addr := inet_addr(PAnsiChar(_multicastGroup));
if (_listeningInterface = '0.0.0.0') or not _isIpAddress(_listeningInterface) then
_mreq.imr_interface.s_addr := htonl(INADDR_ANY)
else
_mreq.imr_interface.s_addr := inet_addr(PAnsiChar(_listeningInterface));
// Joinig multicast group here. Note the _fd variable usage here.
if (setsockopt(_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @_mreq, sizeof(_mreq)) < 0) then Exit;
end;
end;
当我们尝试从微控制器的Keil库中执行此操作时,没有像setsockopt
这样的函数。有igmp_join
个函数不接受套接字变量,而仅接受多播组。
结果,我们无法指定UDP端口和多播组的对应关系,并且每个UDP套接字都成为我们加入的所有组的数据。
正确的方法是以下两种:Keil或Windows?
答案 0 :(得分:1)
两种行为都是正确的。
在哪个套接字上收到的哪个UDP数据包超出了IPv4多播定义的范围。
通常只会违反您只收到加入该群组的流量的期望。而且,所有操作系统都以不同的方式实现此目标。 Linux和Windows的行为不同,并且微控制器中的小型IP堆栈通常具有更简单的行为。
最糟糕的情况是基线:您将在UDP套接字上收到随机UDP数据包,即使是您未加入的组也是如此。但这在实践中通常并不那么糟糕。只是考虑时的指导原则。原因是:操作系统可能会延迟成员资格删除。本地路由器 将延迟成员资格下降。本地路由器将包含错误。
无论如何,您都需要处理接收到意外的UDP数据包,因为网络中的任何人都可以伪造UDP数据包并将其以其他人的名字发送到您的套接字。此外,其他任何人都可以伪造IGMP数据包并将其“加入您”加入任何多播组。
这意味着:在实践中,您始终需要进行应用程序级过滤,并且静默忽略意外的UDP数据包。在大多数IP堆栈上,都有一个(很简单的)选项来确定接收到的UDP数据包的<目的>地址。这对于将流量分流到多个多播组非常有用。
并且由于您现在知道IP堆栈的行为,因此您可以节省一些内存,例如,仅使用一个UDP套接字接收所有UDP通信,并在应用程序端对其进行多路分解。当然,这还取决于您的应用程序。
但是,是的,我知道您对IP多播定义不足感到失望。我和你在一起! :-)