使用 GNAT.Sockets 接收多播流量

chr*_*lof 2 sockets multicast ada gnat windows-10

我正在 Ada 中试验 IP 多播,但似乎没有收到发送到多播组的任何流量。不知何故,我似乎无法让应用程序获取传入的数据包。

我可以验证(使用 Wireshark)是否从我的计算机发送了多播加入,并且我还可以验证是否有数据发送到多播组。

我可以验证操作系统是否已通过 netsh 命令注册了多播加入:

netsh interfaces ip show joins
Run Code Online (Sandbox Code Playgroud)

如果我运行我的程序,则列出的组的引用为 1;如果不运行,则引用为 0。

以下过程显示了我的侦听器,我使用Mcast_IP => "239.255.128.128"和调用它Mcast_Port => "8807"

with GNAT.Sockets;
with Ada.Streams;
with Ada.Text_IO;

   procedure Receive_Multicast (Mcast_IP   : in String;
                                Mcast_Port : in String)
   is
      package GS renames GNAT.Sockets;
      package AS renames Ada.Streams;
      package Tio renames Ada.Text_IO;

      use GS;

      use type Ada.Streams.Stream_Element_Offset;
      Socket  : GS.Socket_Type;
      Address : GS.Sock_Addr_Type;
      Data    : AS.Stream_Element_Array (1 .. 2**16);
      Offset  : AS.Stream_Element_Offset;
      Sender  : GS.Sock_Addr_Type;
   begin
      Address.Addr := Any_Inet_Addr;
      Address.Port := Port_Type'Value (Mcast_Port);

      Create_Socket (Socket => Socket,
                     Family => Family_Inet,
                     Mode   => Socket_Datagram);

      Bind_Socket (Socket, Address);

      --  Set socket options
      Set_Socket_Option (Socket,
                         Socket_Level,
                         (Reuse_Address, True));

      Set_Socket_Option
        (Socket,
         IP_Protocol_For_IP_Level,
         (Multicast_TTL, 1));

      Set_Socket_Option
        (Socket,
        IP_Protocol_For_IP_Level,
        (Multicast_Loop, True));

      Set_Socket_Option
        (Socket,
         IP_Protocol_For_IP_Level,
         (Add_Membership, Inet_Addr (Mcast_IP), Any_Inet_Addr));

      Tio.Put_Line ("Listening for MULTICASTS on port " & Address.Port'Img);

--  Receive the packet from the socket.
      loop
         Tio.Put_Line ("Waiting for incoming packets...");
         Receive_Socket (Socket => Socket,
                         Item   => Data,
                         Last   => Offset,
                         From   => Sender);
         Tio.Put_Line ("Received " & Offset'Img & " bytes.");
      end loop;
   end Receive_Multicast;
Run Code Online (Sandbox Code Playgroud)

该过程一直进行到调用Receive_Socket(这是包中的过程GNAT.Sockets)。但是,即使我可以使用 Wireshark 确认多播流量,对 的调用仍会Receive_Socket阻塞。

更新/解决方案:

上面的代码确实有效,尽管我必须完全卸载卡巴斯基,这显然确实阻止了从我自己的机器发送的多播被接收(即环回)。接受的答案也确实可以完美地工作。

Dee*_*Dee 5

根据 中的示例GNAT.Sockets,下面的代码应该可以工作。我删除了一些选项,因为它们与接收无关。

接收多播广告

procedure Receive_Multicast 
  (IP_Address : String;
   Port       : String);
Run Code Online (Sandbox Code Playgroud)

接收多播.adb

with Ada.Text_IO;
with Ada.Streams;
with GNAT.Sockets;

procedure Receive_Multicast 
  (IP_Address : String;
   Port       : String)
is

   use GNAT.Sockets;

   Address  : Sock_Addr_Type;
   Socket   : Socket_Type;

begin   

   Create_Socket (Socket, Family_Inet, Socket_Datagram);

   Set_Socket_Option
     (Socket => Socket,
      Level  => Socket_Level,
      Option => (Reuse_Address, True));

   Address.Addr := Any_Inet_Addr;
   Address.Port := Port_Type'Value (Port);

   Bind_Socket (Socket, Address);

   --  Join a multicast group

   --  Portability note: On Windows, this option may be set only
   --  on a bound socket.

   Set_Socket_Option
     (Socket => Socket,
      Level  => IP_Protocol_For_IP_Level,
      Option => (Add_Membership, Inet_Addr (IP_Address), Any_Inet_Addr));

   --  Receive the packet from the socket.
   declare

      use Ada.Text_IO;
      use Ada.Streams;

      Data    : Stream_Element_Array (1 .. 2**16);
      Offset  : Stream_Element_Offset;
      Sender  : Sock_Addr_Type;

   begin
      Put_Line ("Waiting for incoming packets...");

      Receive_Socket
        (Socket => Socket,
         Item   => Data,
         Last   => Offset,
         From   => Sender);

      Put_Line ("Received " & Offset'Image & " bytes.");
   end;

end Receive_Multicast;
Run Code Online (Sandbox Code Playgroud)

主程序.adb

with Receive_Multicast;

procedure Main is
begin   
   Receive_Multicast 
     (IP_Address => "239.255.128.128",
      Port       => "8807");   
end Main;
Run Code Online (Sandbox Code Playgroud)

我无法广泛测试代码,但当我打开 Windows PowerShell ISE 时,加载并运行脚本Send-UdpDatagram.ps1(请参阅此 GitHub Gist),然后执行:

PS C:\> Send-UdpDatagram -EndPoint "239.255.128.128" -Port 8807 -Message "testing"
Run Code Online (Sandbox Code Playgroud)

然后 Ada 程序响应:

Waiting for incoming packets...
Received  7 bytes.
[2019-09-29 10:55:58] process terminated successfully, elapsed time: 07.60s
Run Code Online (Sandbox Code Playgroud)

更新

我还使用运行 Raspbian GNU/Linux 10 (buster) 的 Raspberry Pi 测试了示例代码:

  • 在 Raspberry Pi 上gnat安装了 APT 软件包。gprbuild
  • 将代码复制到 Raspberry Pi。
  • 使用 GNAT FSF 编译它 ( gprbuild -p <proj_name>.gpr)。
  • 启动了该程序的四个实例,每个实例都在一个单独的终端中。
  • 像以前一样使用 PowerShell 功能从 Windows 10 主机发出数据包。

结果是一样的:Raspberry Pi 上的所有四个程序实例都收到了数据包。当程序等待数据包时,我可以看到会员资格(另请参阅有关 SO 的这篇文章):

pi@raspberrypi:~ $ netstat -g
IPv6/IPv4 Group Memberships
Interface       RefCnt Group
--------------- ------ ---------------------
[...]
eth0            4      239.255.128.128
[...]
Run Code Online (Sandbox Code Playgroud)
pi@raspberrypi:~ $ netstat -anu | sort -nk4
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
[...]
udp        0      0 0.0.0.0:8807            0.0.0.0:*                          
udp        0      0 0.0.0.0:8807            0.0.0.0:*                          
udp        0      0 0.0.0.0:8807            0.0.0.0:*                          
udp        0      0 0.0.0.0:8807            0.0.0.0:*                          
[...]
Run Code Online (Sandbox Code Playgroud)