请注意,这个问题专门针对 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。
我不明白这些如何导致格式错误的数据包。需要更多信息。