Ada 编程 - 套接字阻塞行为



请注意,这个问题专门针对 Ada 语言和 Ada 的 "g-socket" API

我已经在本地打开了一个套接字,正在侦听传入的连接。 接受连接,我能够通过读取和写入附加到远程套接字的 Stream 对象来通过连接建立某种连贯的数据传输。

问题:

当流附加到 TCP 套接字时,对通用流'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。 所以对我来说,一切都取决于 fwhici 本身称之为操作系统原语的实现C_Sendto

但是,上述 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;
’Write

的默认行为,’Read在ARM13.13.2(8.2(中定义。

在第一种情况下,这相当于两次调用sendto()。如果您设置了TCP_NODELAY,则可能会导致网络上有两个 IP 数据包,如果两个’Write之间有足够长的时间间隔(100 毫秒?否则,数据将被缓冲,直到低级网络软件的 IP 数据包已满(或者,同样,经过足够长的时间间隔(。

如果你有

type Info is record
A : U8;
B : U8;
end record;

然后Info’Write((B => 5, A => 6))将导致两个sendto()调用分别传输一个字节,第一个值为 6(A值;按规范顺序传输,A然后是B(,第二个值为 5。


我不明白这些如何导致格式错误的数据包。需要更多信息。

最新更新