我正在尝试在Erlang中创建一个简单的服务器/应用程序
我的服务器使用gen_tcp:listen(Port, [list, {active, false}, {keepalive, true}, {nodelay, true}])
初始化套接字,客户端与gen_tcp:connect(Server, Port, [list, {active, true}, {keepalive, true}, {nodelay, true}])
连接。
从服务器收到的消息由{tcp, _, [115, 58 | Data]}
等警卫进行测试。
问题是,数据包有时会在发送或接收时连接起来,从而导致意外行为,因为警卫认为下一个数据包是变量的一部分。
有没有办法确保每个数据包都作为单个消息发送到接收进程?
答案 0 :(得分:10)
普通TCP是一种没有数据包边界概念的流媒体协议(如Alnitak所说)。
通常,您使用框架协议以UDP(每个数据包大小有限且可以无序接收)或TCP发送消息。
Framed意味着您在每条消息前面加上一个大小标题(通常为4个字节),表示消息的长度。
在erlang中,您可以将{packet,4}添加到套接字选项中,以在TCP之上获取成帧数据包行为。
假设双方(客户端/服务器)使用{packet,4},那么您将只获得完整的消息。
注意:您将看不到大小标题,erlang会将其从您看到的消息中删除。所以你在顶部的示例匹配仍然可以正常工作
答案 1 :(得分:0)
您可能正在看到Nagle算法的效果,该算法旨在通过将小数据包合并为一个更大的数据包来提高吞吐量。
您需要Erlang等效于在发送套接字上启用TCP_NODELAY
套接字选项。
编辑啊,我看你已经设定了。嗯。 TCP实际上并没有将数据包边界暴露给应用程序层 - 根据定义它是一个流协议。
如果数据包边界很重要,您应该考虑使用UDP,或者确保您发送的每个数据包都以某种方式分隔。例如,在TCP的DNS版本中,每个消息都以2字节长度的标头作为前缀,告诉另一端在下一个块中预期有多少数据。
答案 2 :(得分:0)
您需要为数据包实现分隔符。 一种解决方案是使用特殊字符;或类似的东西。
另一种解决方案是首先发送数据包的大小。 PacketSizeInBytes:身体
然后从您的消息中读取提供的字节数。当你结束时,你得到了整个数据包。
没有人提到TCP也可能将您的邮件分成多个部分(将您的数据包拆分为两个邮件)。
所以第二种解决方案是最好的。但有点难。虽然第一个仍然很好,但限制了您发送带有特殊字符的数据包的能力。但最容易实施。 Ofc这是所有这一切的解决方法。我希望它有所帮助。