Sockets 使用Ada套接字的TCP/IP:如何正确完成数据包?
我试图使用Ada的Sockets库实现远程帧缓冲协议,但在控制发送的数据包长度时遇到了问题 我遵循的是Sockets 使用Ada套接字的TCP/IP:如何正确完成数据包?,sockets,tcp,ada,vnc,rfb-protocol,Sockets,Tcp,Ada,Vnc,Rfb Protocol,我试图使用Ada的Sockets库实现远程帧缓冲协议,但在控制发送的数据包长度时遇到了问题 我遵循的是RFC6143specification(),请参见代码中的注释以获取章节编号 -- Section 7.1.1 String'Write (Comms, Protocol_Version); Put_Line ("Server version: '" & Protocol_Version (1 ..
RFC6143
specification(),请参见代码中的注释以获取章节编号
-- 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节在一个数据包中连续发送,而不是分成两个
我错误地认为从套接字读取”会导致输出数据被清除,但事实似乎并非如此
如何告知Ada的Sockets库“此数据包已完成,请立即发送”取消评论:
我不是协议大师,但根据我自己的经验,对的使用经常被误解
问题似乎是(根据wireshark的说法),我对各种“写入过程”的调用导致字节在套接字上排队而没有被发送
因此,两个或多个数据包作为一个数据包发送,导致数据包格式错误。第7.1.2节和第7.1.3节在一个数据包中连续发送,而不是分成两个
简而言之,TCP不是面向消息的
使用TCP,发送/写入套接字结果只会将数据附加到TCP流。套接字可以在一次或多次交换中自由发送,如果要发送较长的数据,并且要在TCP之上实现面向消息的协议,则可能需要处理消息重建。通常,在消息末尾添加一个特殊的字符序列
进程通过调用TCP并将数据缓冲区作为参数传递来传输数据。TCP将这些缓冲区中的数据打包成段,并调用internet模块将每个段传输到目标TCP。接收TCP将数据段中的数据放入接收用户的缓冲区,并通知接收用户。TCP在其用于确保可靠有序数据传输的段中包含控制信息
另见,引述:
TCP是面向流的连接,而不是面向消息的连接。它没有
信息的概念。当您写出序列化字符串时,它
只能看到无意义的字节序列。TCP可以自由断开
该流向上分为多个片段,它们将在
客户机在这些片段大小的块中。这由你来决定
在另一端重建整个消息
在您的场景中,通常会发送消息长度前缀。
通过这种方式,客户机首先读取长度前缀,这样就可以知道
传入消息应该有多大
或引用:
recv函数只接收1个字节,您可能需要多次调用它才能获得整个有效负载。因此,您需要知道需要多少数据。虽然可以通过关闭连接来表示完成,但这并不是一个好主意
更新:
我还应该提到send函数与recv具有相同的约定:您必须在循环中调用它,因为您不能假设它将发送所有数据。虽然它可能总是在您的开发环境中工作,但这种假设在以后会影响您
禁用Nagle算法,但通常您的期望是错误的。TCP是字节流协议,而不是消息协议。如果您想要消息,您必须自己实现它们。您能否确认读取后字符串Client\u Version
的长度确实是12?并在从流中读取Client\u Requested\u Security\u Type
后指出它的值是什么?@DeeDee,是的,Wireshark确认它正好是12个字节,包括“\n”(它在十六进制转储中清晰地显示)。客户机会回复一条同样格式正确的类似消息。在排队等候多个'Write
调用时,一切都会出错。不,一切都会出错,因为您没有正确阅读。你不能假设一次读取就可以得到一个完整的协议包。它可以提供从一个字节到您提供的缓冲区长度的任何内容,并且该数据可能由一个或多个数据包的一小部分组成,或者同时由这两个数据包组成。你必须在阅读时处理好所有这些。您在发送端所能做的任何事情都不能免除该要求。@user207421。谢谢,我想我现在明白了。我需要为任何“消息”创建足够大的字符缓冲区,并在循环中手动读取(或写入)顺序字节,直到传输正确数量的字符。如果我是用C代码写的,我会本能地做这种事情。Ada包g-socket.ads
包含了一个关于如何使用TCP和UDP的很好的示例,但是遗憾地忽略了这些缓冲区需要以艰难的方式维护。这表明Ada的String'Read(stream,stru buffer)
正在阻塞,直到缓冲区已满!不太确定最后一句关于“发送函数”的评论;(a) 它是sendto()
,不是吗?(b) 对于TCP流,不发送整个消息的唯一原因是一个错误?(TBH粗略复制答案(这是有争议的))@SimonWright不适用于TCP:根据BSD手册页,send()
和write()
@user207421,sendto()
可以在连接状态下使用(即TCP?),&OP的Ada套接字库可执行此操作。