我正在尝试使用在开始之前附加的每条消息的大小来通过TCP发送和接收消息。
说,前三个字节将是长度,稍后将是消息:
作为一个小例子:
005Hello003Hey002Hi
我将使用此方法执行大型消息,但因为缓冲区大小将是一个常量整数,例如200
字节。因此,有可能不会收到完整的消息,例如而不是005Hello
我得到005He
,也不会收到完整的长度,例如我在消息中得到2个字节的长度。
所以,为了解决这个问题,我需要等待下一条消息,然后将追加到不完整的消息等。
我的问题是:我是唯一一个有这些困难的人,彼此追加消息,追加长度等等......让它们完整或者这真的通常是我们如何处理TCP上的各个消息?或者,如果有更好的方法?
答案 0 :(得分:2)
您所看到的是100%正常的TCP行为。完全可以预期,在您收到"消息之前,您将循环接收字节。 (无论在你的背景下意味着什么)。它是从低级别TCP字节流转向更高级别概念(如" message")的工作的一部分。
&& 34; usr"就在上面。您可能有更高级别的抽象。如果它们合适,请使用它们以避免重新发明轮子。
答案 1 :(得分:0)
因此,有可能无法接收完整的消息,例如 而不是005Hello我得到005He,也可能收到完整的长度 例如我在消息中得到2个字节的长度。
是。 TCP为每次读取提供至少一个字节,这就是全部。
或者这通常是我们需要如何处理TCP上的各个消息?或者,如果有更好的方法?
尝试使用更高级别的原语。例如,BinaryReader
允许您准确读取N个字节(它将在内部循环)。 StreamReader
让你忘记了TCP的这种特性。
更好的是使用更高级别的抽象,例如HTTP(请求/响应模式 - 非常常见),protobuf作为序列化格式或自动化几乎所有传输层问题的Web服务。
如果可以避免,不要做TCP。
答案 2 :(得分:0)
所以,为了解决这个问题,我需要等待下一条消息并将其附加到不完整的消息等。
是的,这就是套接字级代码的完成方式。对于每个套接字,您希望分配一个至少与内核套接字接收缓冲区大小相同的缓冲区,以便您可以在一次read/recv/resvmsg
调用中读取整个内核缓冲区。从循环中的套接字读取可能会使应用程序中的其他套接字处于饥饿状态(这就是为什么默认情况下将epoll
更改为级别触发的原因,因为默认的边缘触发的强制应用程序编写器在循环中读取。 / p>
第一个未完成的消息始终保存在缓冲区的开头,读取套接字继续在缓冲区中的下一个空闲字节,以便它自动附加到不完整的消息。
读取完成后,通常会使用指向缓冲区中所有读取数据的指针调用更高级别的回调。该回调应该使用缓冲区中的所有完整消息并返回它消耗的字节数(如果只有不完整的消息,则可以为0)。缓冲区管理代码应该memmove
剩余的未消耗字节(如果有)到缓冲区的开头。或者,可以使用环形缓冲区来避免移动那些未消耗的字节,但在这种情况下,更高级别的代码应该能够处理环形缓冲区迭代器,它可能还没准备好。因此,保持缓冲线性可能是最方便的选择。