数据包有时会串联
我正在尝试在 Erlang 中制作一个简单的服务器/应用程序。
我的服务器使用 gen_tcp:listen(Port, [list, {active, false}, {keepalive, true}, {nodelay, true}]) 初始化套接字,客户端使用 gen_tcp 连接:connect(服务器,端口,[list, {active, true}, {keepalive, true}, {nodelay, true}])。 从服务器接收到的消息由守卫进行测试,例如 {tcp, _, [115, 58 |数据]}
。
问题是,数据包有时在发送或接收时会被串联,从而导致意外行为,因为防护程序将下一个数据包视为变量的一部分。
有没有办法确保每个数据包都作为单个消息发送到接收进程?
I'm trying to make a simple server/application in Erlang.
My server initialize a socket with gen_tcp:listen(Port, [list, {active, false}, {keepalive, true}, {nodelay, true}])
and the clients connect with gen_tcp:connect(Server, Port, [list, {active, true}, {keepalive, true}, {nodelay, true}])
.
Messages received from the server are tested by guards such as {tcp, _, [115, 58 | Data]}
.
Problem is, packets sometimes get concatenated when sent or received and thus cause unexpected behaviors as the guards consider the next packet as part of the variable.
Is there a way to make sure every packet is sent as a single message to the receiving process?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
普通 TCP 是一种流协议,没有数据包边界的概念(就像 Alnitak 所说)。
通常,您可以使用帧协议以 UDP(每个数据包大小有限并且可能会乱序接收)或 TCP 形式发送消息。
成帧意味着您为每条消息添加一个大小标头(通常为 4 个字节)作为前缀,指示消息的长度。
在 erlang 中,您可以将 {packet,4} 添加到套接字选项中,以获取 TCP 之上的帧数据包行为。
假设双方(客户端/服务器)都使用 {packet,4} 那么你只会得到完整的消息。
注意:您不会看到大小标头,erlang 会将其从您看到的消息中删除。所以你在顶部的示例匹配应该仍然可以正常工作
Plain TCP is a streaming protocol with no concept of packet boundaries (like Alnitak said).
Usually, you send messages in either UDP (which has limited per-packet size and can be received out of order) or TCP using a framed protocol.
Framed meaning you prefix each message with a size header (usualy 4 bytes) that indicates how long the message is.
In erlang, you can add {packet,4} to your socket options to get framed packet behavior on top of TCP.
assuming both sides (client/server) use {packet,4} then you will only get whole messages.
note: you won't see the size header, erlang will remove it from the message you see. So your example match at the top should still work just fine
您可能已经看到了 Nagle 算法的效果,该算法旨在通过将小数据包合并为单个较大的数据包来提高吞吐量。
您需要在发送套接字上启用
TCP_NODELAY
套接字选项的 Erlang 等效项。编辑啊,我看到你已经设置好了。唔。 TCP 实际上并不向应用层公开数据包边界 - 根据定义,它是一个流协议。
如果数据包边界很重要,您应该考虑使用 UDP,或者确保您发送的每个数据包都以某种方式分隔。例如,在 DNS 的 TCP 版本中,每个消息都以 2 字节长度的标头作为前缀,该标头告诉另一端在下一个块中期望有多少数据。
You're probably seeing the effects of Nagle's algorithm, which is designed to increase throughput by coalescing small packets into a single larger packet.
You need the Erlang equivalent of enabling the
TCP_NODELAY
socket option on the sending socket.EDIT ah, I see you already set that. Hmm. TCP doesn't actually expose packet boundaries to the application layer - by definition it's a stream protocol.
If packet boundaries are important you should consider using UDP instead, or make sure that each packet you send is delimited in some manner. For example, in the TCP version of DNS each message is prefixed by a 2 byte length header, which tells the other end how much data to expect in the next chunk.
您需要为数据包实现分隔符。
一种解决方案是使用特殊字符;或类似的东西。
另一种解决方案是先发送数据包的大小。
PacketSizeInBytes:Body
然后从消息中读取提供的字节数。当你到达最后时,你就得到了整个包裹。
没有人提到 TCP 也可能将您的消息拆分为多个部分(将您的数据包拆分为两条消息)。
所以第二种解决方案是最好的。但有点难。虽然第一个仍然很好,但限制了您发送带有特殊字符的数据包的能力。但最容易实现。 Ofc 有一个解决方法来解决所有这些问题。我希望它有帮助。
You need to implement a delimiter for your packets.
One solution is to use a special character ; or something similar.
The other solution is to send the size of the packet first.
PacketSizeInBytes:Body
Then read the provided amount of bytes from your message. When you're at the end you got your whole packet.
Nobody mentions that TCP may also split your message into multiple pieces (split your packet into two messages).
So the second solution is the best of all. But a little hard. While the first one is still good but limits your ability to send packets with special characters. But the easiest to implement. Ofc theres a workaround for all of this. I hope it helps.