IP组播+ UDP:我应该接收所有组的数据吗?

时间:2019-02-05 12:52:13

标签: sockets delphi multicast keil

通过UDP进行IP组播的正确行为是什么?

想象一下自己的情况:

  1. 我们尝试通过UDP谈IP协议。
  2. 我们打开一个具有特定端口(例如60001)的UDP套接字
  3. 然后我们将套接字加入某个多播组(例如239.192.0.1)
  4. 我们用端口60002打开另一个UDP套接字
  5. 然后我们将套接字加入其他多播组(例如239.192.0.2)

在Windows中使用Winsock完成此操作时,只有在同时满足两个条件的情况下,每个套接字才变为数据:

  • UDP端口匹配
  • 和多播组匹配。

这是一些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?

1 个答案:

答案 0 :(得分:1)

两种行为都是正确的。

在哪个套接字上收到的哪个UDP数据包超出了IPv4多播定义的范围。

通常只会违反您只收到加入该群组的流量的期望。而且,所有操作系统都以不同的方式实现此目标。 Linux和Windows的行为不同,并且微控制器中的小型IP堆栈通常具有更简单的行为。

最糟糕的情况是基线:您将在UDP套接字上收到随机UDP数据包,即使是您加入的组也是如此。但这在实践中通常并不那么糟糕。只是考虑时的指导原则。原因是:操作系统可能会延迟成员资格删除。本地路由器 将延迟成员资格下降。本地路由器将包含错误。

无论如何,您都需要处理接收到意外的UDP数据包,因为网络中的任何人都可以伪造UDP数据包并将其以其他人的名字发送到您的套接字。此外,其他任何人都可以伪造IGMP数据包并将其“加入您”加入任何多播组。

这意味着:在实践中,您始终需要进行应用程序级过滤,并且静默忽略意外的UDP数据包。在大多数IP堆栈上,都有一个(很简单的)选项来确定接收到的UDP数据包的<目的>地址。这对于将流量分流到多个多播组非常有用。

并且由于您现在知道IP堆栈的行为,因此您可以节省一些内存,例如,仅使用一个UDP套接字接收所有UDP通信,并在应用程序端对其进行多路分解。当然,这还取决于您的应用程序。

但是,是的,我知道您对IP多播定义不足感到失望。我和你在一起! :-)