sockets - Ada Programming - 套接字阻塞行为
问题描述
请注意,这个问题专门针对 Ada 语言和 Ada 的“g-socket”API
我在本地打开了一个 Socket 并正在监听传入的连接。连接被接受,并且我能够通过读取和写入连接到远程套接字的 Stream 对象来在连接上建立某种连贯的数据传输。
问题:
当 Stream 附加到 TCP Socket 时,是否每次调用通用流'Write
过程都会立即发送数据包?
示例 A:
-- two separate `'Write` calls always seems to generate two packets of 1 byte each
U8'Write (Comms, Number_Of_Security_Types);
U8'Write (Comms, Security_Type_None);
示例 B:
-- One `'Write` call that happens to send the same data formatted as a 16 bit value is sent as a single packet.
U16'Write (Comms,
(U16 (Number_Of_Security_Types) * 16#100#) +
U16 (Security_Type_None)
);
示例 C:
-- This is a complex record with a further record nested within it.
-- its `'Write` callback is implemented as a series of many sequential `Integer'Write` calls...
Server_Init'Write (Comms, Server_Init_Rec);
示例 A 和 C 导致 Wireshark 检测到格式错误的数据包,但示例 B 创建了一个精心设计的数据包,没有任何问题。
这种行为似乎是确定性的,但我找不到任何关于'Write
--> Stream --> Socket 安排关于如何以及何时分派数据包的连贯文档。
解决方案
根据此链接,TCP 流写入的底层代码应如下所示。如您所见,有一个循环尝试发送数据,直到所有内容都传入Send_Socket
. 所以对我来说,一切都取决于C_Sendto
实现 fwhici 本身调用操作系统原语
但是,所述 8 位的单个写入并不能保证它会对应于包含这些数据包的网络数据包(因为 TCP 的本质)。
-----------
-- Write --
-----------
procedure Write
(Stream : in out Datagram_Socket_Stream_Type;
Item : Ada.Streams.Stream_Element_Array)
is
First : Ada.Streams.Stream_Element_Offset := Item'First;
Index : Ada.Streams.Stream_Element_Offset := First - 1;
Max : constant Ada.Streams.Stream_Element_Offset := Item'Last;
begin
loop
Send_Socket <== try to send until all content of write sent ?
(Stream.Socket,
Item (First .. Max),
Index,
Stream.To);
-- Exit when all or zero data sent. Zero means that the
-- socket has been closed by peer.
exit when Index < First or else Index = Max;
First := Index + 1;
end loop;
if Index /= Max then
raise Socket_Error;
end if;
end Write;
-- [...]
procedure Send_Socket
(Socket : Socket_Type;
Item : Ada.Streams.Stream_Element_Array;
Last : out Ada.Streams.Stream_Element_Offset;
To : Sock_Addr_Type)
is
use type Ada.Streams.Stream_Element_Offset;
Res : C.int;
Sin : aliased Sockaddr_In;
Len : aliased C.int := Sin'Size / 8;
begin
Sin.Sin_Family := C.unsigned_short (Families (To.Family));
Sin.Sin_Addr := To_In_Addr (To.Addr);
Sin.Sin_Port := Port_To_Network (C.unsigned_short (To.Port));
Res := C_Sendto -- <== where things happen
(C.int (Socket),
Item (Item'First)'Address,
Item'Length, 0,
Sin'Unchecked_Access,
Len);
if Res = Failure then
Raise_Socket_Error (Socket_Errno);
end if;
Last := Item'First + Ada.Streams.Stream_Element_Offset (Res - 1);
end Send_Socket;
https://www2.adacore.com/gap-static/GNAT_Book/html/rts/g-socthi__adb.htm
--------------
-- C_Sendto --
--------------
function C_Sendto
(S : C.int;
Msg : System.Address;
Len : C.int;
Flags : C.int;
To : Sockaddr_In_Access;
Tolen : C.int)
return C.int
is
Res : C.int;
begin
loop
Res := Syscall_Sendto (S, Msg, Len, Flags, To, Tolen);
exit when Thread_Blocking_IO
or else Res /= Failure
or else Table (S).Non_Blocking
or else Errno /= Constants.EWOULDBLOCK;
delay Quantum;
end loop;
return Res;
end C_Sendto;
推荐阅读
- java - 如何通过 GRADLE 强制库使用内部依赖项而不是外部依赖项?
- reactjs - MS Teams 连接器保存按钮给出错误:从连接器收到错误
- python - 使用 matplotlib 在最终图中裁剪出绘图区域
- python - 印刷 。在字符串中的每个字符之前
- python - Python-Django 导入 csv 到 Jupiter 注意本地
- android - 文字转语音正在活动中工作,但不在后台工作
- flutter - 问:如何在新的 rive 中反向播放 OneShotAnimations
- express - Express 的压缩中间件未执行
- gitlab-ci - 我怎样才能使连续工作可以中断?
- amazon-web-services - 触发物化视图刷新 - AWS Lambda