0

我正在尝试使用 Ada 的 Sockets 库来实现远程帧缓冲区协议,但我无法控制我正在发送的数据包的长度。

我正在遵循RFC 6143规范(https://tools.ietf.org/pdf/rfc6143.pdf),请参阅代码中的注释以获取节号...

          --  Section 7.1.1
          String'Write (Comms, Protocol_Version);
          Put_Line ("Server version: '"
            & Protocol_Version (1 .. 11) & "'");

          String'Read (Comms, Client_Version);
          Put_Line ("Client version: '"
            & Client_Version (1 .. 11) & "'");

          --  Section 7.1.2
          --  Server sends security types
          U8'Write (Comms, Number_Of_Security_Types);
          U8'Write (Comms, Security_Type_None);


          --  client replies by selecting a security type
          U8'Read (Comms, Client_Requested_Security_Type);
          Put_Line ("Client requested security type: "
            & Client_Requested_Security_Type'Image);

          --  Section 7.1.3
          U32'Write (Comms, Byte_Reverse (Security_Result));

          --  Section 7.3.1
          U8'Read (Comms, Client_Requested_Shared_Flag);
          Put_Line ("Client requested shared flag: "
            & Client_Requested_Shared_Flag'Image);


          Server_Init'Write (Comms, Server_Init_Rec);

问题似乎是(根据wireshark)我对各种'Write程序的调用导致字节在套接字上排队而没有被发送。

因此,两个或多个数据包的数据被作为一个发送并导致格式错误的数据包。第 7.1.2 和 7.1.3 节在一个数据包中连续发送,而不是分成两个。

我错误地认为'Read来自套接字的 ing 会导致传出数据被刷新,但事实并非如此。

我如何告诉 Ada 的 Sockets 库“这个数据包已经完成,现在就发送”?

4

1 回答 1

0

强调https://stackoverflow.com/users/207421/user207421评论:

我不是协议专家,但根据我自己的经验,TCP(参见 RFC793)的使用经常被误解。

问题似乎是(根据wireshark)我对各种“写程序”的调用导致字节在套接字上排队而没有被发送。

因此,两个或多个数据包的数据被作为一个发送并导致格式错误的数据包。第 7.1.2 和 7.1.3 节在一个数据包中连续发送,而不是分成两个。

简而言之,TCP 不是面向消息的。

使用 TCP,发送/写入套接字结果只会将数据附加到 TCP 流。套接字可以在一次或多次交换中自由发送,如果您有很长的数据要发送并且要在 TCP 之上实现面向消息的协议,则可能需要处理消息重构。通常,在消息末尾添加消息结束特殊字符序列。

进程通过调用 TCP 并将数据缓冲区作为参数传递来传输数据。TCP 将这些缓冲区中的数据打包成段,并调用 Internet 模块将每个段传输到目标 TCP。接收 TCP 将数据段中的数据放入接收用户的缓冲区并通知接收用户。TCP 在它们用来确保可靠有序数据传输的段中包含控制信息。

另请参阅https://stackoverflow.com/a/11237634/7237062,引用:

TCP 是面向流的连接,而不是面向消息的连接。它没有消息的概念。当你写出你的序列化字符串时,它只会看到一个毫无意义的字节序列。TCP 可以自由地将该流分解为多个片段,并且它们将在客户端以这些片段大小的块的形式接收。您可以在另一端重建整个消息。

在您的场景中,通常会发送消息长度前缀。这样,客户端首先读取长度前缀,然后它就可以知道传入消息应该有多大。

TCP Connection Seems to Receive Incomplete Data,引用:

recv 函数可以接收低至 1 个字节,您可能需要多次调用它才能获取整个有效负载。因此,您需要知道您期望的数据量。尽管您可以通过关闭连接来表示完成,但这并不是一个好主意。

更新:

我还应该提到 send 函数与 recv 具有相同的约定:您必须在循环中调用它,因为您不能假设它会发送您的所有数据。虽然它可能总是在您的开发环境中工作,但这种假设会在以后给您带来麻烦。

于 2019-10-13T09:40:02.887 回答