使用 GNAT.Sockets 接收多播流量
Receiving multicast traffic using GNAT.Sockets
我正在 Ada 中试验 IP 多播,但似乎没有收到任何发送到多播组的流量。不知何故,我似乎无法让应用程序获取传入的数据包。
我可以验证(使用 Wireshark)多播加入是从我的计算机发送的,而且我还可以验证是否有数据正在发送到多播组。
我可以验证 OS 是否已通过 netsh 命令注册了多播加入:
netsh interfaces ip show joins
如果我运行我的程序,我的组被列出并引用 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;
该过程一直运行到 Receive_Socket
调用(这是 GNAT.Sockets
包中的一个过程)。但是,即使我可以使用 Wireshark 确认多播流量,对 Receive_Socket
的调用仍然阻塞。
UPDATE/SOLUTION:
上面的代码确实有效,尽管我必须完全卸载卡巴斯基,这显然确实阻止了从我自己的机器发送的多播被接收(即环回)。接受的答案也确实完美无缺。
基于 GNAT.Sockets
中的示例,下面的代码应该可以工作。我删除了一些与接收无关的选项。
receive_multicast.ads
procedure Receive_Multicast
(IP_Address : String;
Port : String);
receive_multicast.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;
main.adb
with Receive_Multicast;
procedure Main is
begin
Receive_Multicast
(IP_Address => "239.255.128.128",
Port => "8807");
end Main;
我无法广泛测试代码,但是当我打开 Windows PowerShell ISE 时,加载并 运行 脚本 Send-UdpDatagram.ps1
(参见 this GitHub Gist),然后执行:
PS C:\> Send-UdpDatagram -EndPoint "239.255.128.128" -Port 8807 -Message "testing"
然后 Ada 程序响应:
Waiting for incoming packets...
Received 7 bytes.
[2019-09-29 10:55:58] process terminated successfully, elapsed time: 07.60s
更新
我还用 Raspberry Pi 运行ning Raspbian GNU/Linux 10 (buster):
测试了示例代码
- 在 Raspberry Pi.
上安装了 APT 软件包 gnat
和 gprbuild
- 已将代码复制到 Raspberry Pi。
- 使用 GNAT FSF 编译它 (
gprbuild -p <proj_name>.gpr
)。
- 启动了程序的四个实例,每个都在一个单独的终端中。
- 像以前一样使用 PowerShell 函数从 Windows 10 主机发出数据包。
结果是一样的:Raspberry Pi 上的所有四个程序实例都收到了数据包。当程序在等待数据包时,我可以看到成员资格(另见 this post 上的 SO):
pi@raspberrypi:~ $ netstat -g
IPv6/IPv4 Group Memberships
Interface RefCnt Group
--------------- ------ ---------------------
[...]
eth0 4 239.255.128.128
[...]
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:*
[...]
我正在 Ada 中试验 IP 多播,但似乎没有收到任何发送到多播组的流量。不知何故,我似乎无法让应用程序获取传入的数据包。
我可以验证(使用 Wireshark)多播加入是从我的计算机发送的,而且我还可以验证是否有数据正在发送到多播组。
我可以验证 OS 是否已通过 netsh 命令注册了多播加入:
netsh interfaces ip show joins
如果我运行我的程序,我的组被列出并引用 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;
该过程一直运行到 Receive_Socket
调用(这是 GNAT.Sockets
包中的一个过程)。但是,即使我可以使用 Wireshark 确认多播流量,对 Receive_Socket
的调用仍然阻塞。
UPDATE/SOLUTION:
上面的代码确实有效,尽管我必须完全卸载卡巴斯基,这显然确实阻止了从我自己的机器发送的多播被接收(即环回)。接受的答案也确实完美无缺。
基于 GNAT.Sockets
中的示例,下面的代码应该可以工作。我删除了一些与接收无关的选项。
receive_multicast.ads
procedure Receive_Multicast
(IP_Address : String;
Port : String);
receive_multicast.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;
main.adb
with Receive_Multicast;
procedure Main is
begin
Receive_Multicast
(IP_Address => "239.255.128.128",
Port => "8807");
end Main;
我无法广泛测试代码,但是当我打开 Windows PowerShell ISE 时,加载并 运行 脚本 Send-UdpDatagram.ps1
(参见 this GitHub Gist),然后执行:
PS C:\> Send-UdpDatagram -EndPoint "239.255.128.128" -Port 8807 -Message "testing"
然后 Ada 程序响应:
Waiting for incoming packets...
Received 7 bytes.
[2019-09-29 10:55:58] process terminated successfully, elapsed time: 07.60s
更新
我还用 Raspberry Pi 运行ning Raspbian GNU/Linux 10 (buster):
测试了示例代码- 在 Raspberry Pi. 上安装了 APT 软件包
- 已将代码复制到 Raspberry Pi。
- 使用 GNAT FSF 编译它 (
gprbuild -p <proj_name>.gpr
)。 - 启动了程序的四个实例,每个都在一个单独的终端中。
- 像以前一样使用 PowerShell 函数从 Windows 10 主机发出数据包。
gnat
和 gprbuild
结果是一样的:Raspberry Pi 上的所有四个程序实例都收到了数据包。当程序在等待数据包时,我可以看到成员资格(另见 this post 上的 SO):
pi@raspberrypi:~ $ netstat -g
IPv6/IPv4 Group Memberships
Interface RefCnt Group
--------------- ------ ---------------------
[...]
eth0 4 239.255.128.128
[...]
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:*
[...]