You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用GNAT.Sockets在Ada中从UDP套接字获取有效数据?

Ada UDP服务器:监听、接收与回复消息

核心实现思路

UDP是无连接协议,无需Listen_Socket或Accept_Socket这类TCP专用操作,直接通过Receive_Socket读取数据包,再用Send_Socket回复即可。以下是完整的分步实现:


1. 接收UDP消息

使用GNAT.Sockets.Receive_Socket从绑定的套接字读取数据,该过程需要传入:

  • 套接字对象
  • 存储数据的Stream_Element_Array缓冲区
  • 存储发送方地址的Sock_Addr_Type变量
  • 输出参数Last,标记缓冲区中实际被填充的最后一个元素位置

在你的基础代码上补充接收逻辑:

with GNAT.Sockets; use GNAT.Sockets;
with Ada.Text_IO; use Ada.Text_IO;

procedure UDP_Server is
   Sock : Socket_Type;
   Addr : Sock_Addr_Type (Family_Inet);
   Buffer : Stream_Element_Array (1 .. 1024); -- 定义1KB缓冲区
   Last : Stream_Element_Offset;
   Sender_Addr : Sock_Addr_Type (Family_Inet);
begin
   Create_Socket (Sock, Family_Inet, Socket_Datagram);
   Addr.Addr := Any_Inet_Addr;
   Addr.Port := 12345;
   Bind_Socket (Sock, Addr);
   Put_Line ("UDP服务器已启动,监听端口12345...");

   loop -- 持续监听消息
      Receive_Socket (Socket => Sock,
                      Item => Buffer,
                      Last => Last,
                      From => Sender_Addr);
      -- 此时Buffer(1..Last)就是收到的原始数据
      Put_Line ("收到来自" & Image (Sender_Addr.Addr) & ":" & Port_Type'Image (Sender_Addr.Port) & "的消息");
   end loop;
end UDP_Server;

2. 安全转换Stream_Element_Array到字符串

避免依赖实现细节的Unchecked_Conversion,改用符合Ada标准的显式类型转换:

function SEA_To_String (SEA : Stream_Element_Array) return String is
   Result : String (1 .. Natural (SEA'Length));
begin
   for I in SEA'Range loop
      Result (Natural (I - SEA'First + 1)) := Character'Val (Stream_Element'Pos (SEA (I)));
   end loop;
   return Result;
end SEA_To_String;

这个函数通过Stream_Element'Pos获取字节数值,再用Character'Val转换为对应字符,完全遵循Ada标准,不依赖平台特定实现。

将其集成到接收逻辑中:

-- 在Receive_Socket调用后添加
declare
   Received_String : String := SEA_To_String (Buffer (1 .. Last));
begin
   Put_Line ("消息内容:" & Received_String);
end;

3. 回复UDP消息

使用GNAT.Sockets.Send_Socket将回复发送回发送方,先将字符串转换为Stream_Element_Array:

function String_To_SEA (Str : String) return Stream_Element_Array is
   Result : Stream_Element_Array (1 .. Stream_Element_Offset (Str'Length));
begin
   for I in Str'Range loop
      Result (Stream_Element_Offset (I - Str'First + 1)) := Stream_Element'Val (Character'Pos (Str (I)));
   end loop;
   return Result;
end String_To_SEA;

然后在接收后添加回复逻辑:

declare
   Reply_String : String := "已收到你的消息:" & Received_String;
   Reply_SEA : Stream_Element_Array := String_To_SEA (Reply_String);
begin
   Send_Socket (Socket => Sock,
                Item => Reply_SEA,
                To => Sender_Addr);
   Put_Line ("已回复消息:" & Reply_String);
end;

完整可运行代码

with GNAT.Sockets; use GNAT.Sockets;
with Ada.Text_IO; use Ada.Text_IO;

procedure UDP_Server is
   Sock : Socket_Type;
   Addr : Sock_Addr_Type (Family_Inet);
   Buffer : Stream_Element_Array (1 .. 1024);
   Last : Stream_Element_Offset;
   Sender_Addr : Sock_Addr_Type (Family_Inet);

   function SEA_To_String (SEA : Stream_Element_Array) return String is
      Result : String (1 .. Natural (SEA'Length));
   begin
      for I in SEA'Range loop
         Result (Natural (I - SEA'First + 1)) := Character'Val (Stream_Element'Pos (SEA (I)));
      end loop;
      return Result;
   end SEA_To_String;

   function String_To_SEA (Str : String) return Stream_Element_Array is
      Result : Stream_Element_Array (1 .. Stream_Element_Offset (Str'Length));
   begin
      for I in Str'Range loop
         Result (Stream_Element_Offset (I - Str'First + 1)) := Stream_Element'Val (Character'Pos (Str (I)));
      end loop;
      return Result;
   end String_To_SEA;

begin
   Create_Socket (Sock, Family_Inet, Socket_Datagram);
   Addr.Addr := Any_Inet_Addr;
   Addr.Port := 12345;
   Bind_Socket (Sock, Addr);
   Put_Line ("UDP服务器已启动,监听端口12345...");

   loop
      Receive_Socket (Socket => Sock,
                      Item => Buffer,
                      Last => Last,
                      From => Sender_Addr);

      declare
         Received_String : String := SEA_To_String (Buffer (1 .. Last));
         Reply_String : String := "已收到你的消息:" & Received_String;
         Reply_SEA : Stream_Element_Array := String_To_SEA (Reply_String);
      begin
         Put_Line ("收到来自" & Image (Sender_Addr.Addr) & ":" & Port_Type'Image (Sender_Addr.Port) & "的消息:" & Received_String);
         Send_Socket (Socket => Sock,
                      Item => Reply_SEA,
                      To => Sender_Addr);
         Put_Line ("已回复:" & Reply_String);
      end;
   end loop;
exception
   when Socket_Error =>
      Put_Line ("套接字错误");
      Close_Socket (Sock);
end UDP_Server;

关键说明

  • Receive_Vector适用于一次接收多个UDP数据包(分散-聚集IO),普通单包场景用Receive_Socket足够。
  • 不要使用TCP的流操作('Read/'Input),UDP是面向数据包的协议,流操作会破坏数据包边界。
  • 上述转换函数完全符合Ada标准,即使Stream_Element的实现大小不是8位,只要字符编码匹配,就能正确转换。

内容的提问来源于stack exchange,提问作者Mark Gardner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:10:20