如何确定套接字缓冲区中是不是接收到所有数据?
Posted
技术标签:
【中文标题】如何确定套接字缓冲区中是不是接收到所有数据?【英文标题】:How to determine if all data is received in the socket buffer?如何确定套接字缓冲区中是否接收到所有数据? 【发布时间】:2012-12-02 18:59:15 【问题描述】:是否可以确定TServerSocket
Receive Buffer 中是否包含所有数据?
【问题讨论】:
为什么要使用极其过时的技术?TServerSocket
和 TClientSocket
已被折旧,仅用于向后兼容。此外,您需要比这更具体的日志。
是的,在 Mastering Delphi 6/7 中,我读到 TTcpClient/Server 是他们的替代品,但我发现很难使用它们 -_- (包括 Indy 的)
根据我的经验,Indy 和 Synapse 更容易使用
我希望我能及时发现我上面的错字以纠正它......
TTcpClient/Server
是 Borland 尝试在 D6 中实现跨平台套接字以支持 Kylix。 Kylix 已经死了,尽管这些组件仍然存在,但它们已被 Indy 取代。
【参考方案1】:
协议就是这样。
可以是已定义的结束标记。例如HTTP 中的 CR LF CR LF 包含请求长度的标头。【讨论】:
不,我的意思是我如何确定被解雇的 OnRead 是最后一个?我认为这可能是不可能的,我没有问如何确定数据标签的结尾或类似的东西...... @0x90 这是不可能的。 OnRead 事件可以用另一个数据包再次触发。这使得 TServerSocket 比 Indy 或 Synapse 更难处理,您的代码要求库读取到终止符或已知数据长度(或直到发生错误/超时)。使用 TServerSocket / TClientSocket,没有将“逻辑连接”数据分离到 OnRead 事件调用中。 @0x90:您必须跟踪您正在实施的任何协议的状态。当您收到OnRead
事件时,最好读取所有可用字节并将它们附加到您选择的缓冲区的末尾,然后您可以根据需要解析缓冲区,使用您的协议状态机了解什么在缓冲区中。仅从缓冲区解析出已完成的消息,并将缓冲区中的任何剩余字节留给以后的OnRead
事件。【参考方案2】:
没有办法确定 OnRead 是否已读完,因为根据定义没有结束。这意味着您不应该简单地发送没有特殊信息的二进制数据。例如,您可以先发送字节数(例如,4 字节无符号整数),然后再发送您希望发送的字节数。
在接收方,您将首先读取 4 个字节,现在您知道可以预期多少字节。
【讨论】:
这就是我已经在做的事情,使用 Remmy 的示例代码:***.com/questions/13261486/… 但是,由于 OnRead 多次触发......我想确定最后一次是什么时候,这样我就可以阅读一下子。 这不是这样做的方法。操作系统决定何时向您发送另一块蛋糕。 OnRead 将读取操作系统为您提供的内容并对其进行缓冲。换句话说:你必须收集它并做出反应。例如。当您收到“宣布”字节的最后一部分时,您可以处理它们。 @0x90: alzaimar 是对的。当您收到OnRead
事件时,您必须读取套接字上可用的所有内容并将其缓冲到某处。然后,您可以根据您正在实施的任何协议解析缓冲区以查看它是否包含完整的消息。如果您找到一条已完成的消息,请将其从缓冲区中删除并根据需要对其进行处理,然后在缓冲区不再包含已完成的消息时再次重复。将任何未完成的字节留在缓冲区中,以便稍后OnRead
事件可以将新字节附加到它。对收到的每个 OnRead
事件重复解析。【参考方案3】:
如果您打算从服务器上的客户端读取数据,典型的方法是使用缓冲区字符串(每个客户端套接字)和解析该字符串的循环。例如,我假设您创建一个对象来包装每个服务器/客户端套接字。在此对象中,创建一个名为 Buffer
的字符串。每当您从客户端接收数据时,将新数据附加到此字符串的末尾。然后,一个单独的进程(一个线程)可以解析这个字符串并检查一个完整的数据包。但是,读取数据包大小的方法完全取决于您。
无法保证服务器会按照发送顺序接收数据时触发事件。客户端可能向服务器发送 4 个数据包,服务器只为所有数据触发一个事件,或者客户端可能发送 1 个数据包,服务器为部分数据触发两个事件。您有责任不断读取传入的数据以确保完整性,具体取决于您的套接字通信的性质。
alzaimar 的答案是你需要做的。假设一个完整的数据包是这样的字符串:
This is some string.
在解析这样的字符串时,使用分隔符是有风险的。因此,取而代之的是,获取该字符串的大小并将其添加到您的数据包上,并在该大小和数据之间仅使用一个分隔符。由于上面的字符串有 20 个字符长,实际的数据包应该是这样的:
20:This is some string.
...冒号 (:) 是大小和数据之间的分隔符。首先,您检查以确保分隔符存在。如果是这样,则将所有内容复制到它上面。如果它是一个有效数字,则意味着您需要从缓冲区中读取那么多字符。接下来,检查缓冲区是否有那么多可用数据。如果是这样,则从缓冲区中获取该数据。请记住仅在必要时从缓冲区中删除数据 - 如果您愿意,我可以提供更详细的代码示例。
【讨论】:
以上是关于如何确定套接字缓冲区中是不是接收到所有数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Linux 中创建原始套接字而不缓冲接收数据包?是不是可以?