在gen_tcp:recv(Socket, 0) 中,您在询问内核:“给我接收缓冲区中现在可用的所有数据”。内核也可以免费为您提供更少的内容。即使对于一个相当快的链接,您可能会在 TCP 连接上点击 slow start,所以一开始您不会获得太多数据。
解决方案是自己做缓冲。您将不得不从底层套接字中获取数据,直到您有足够的时间来构造消息。因此,二进制协议在流之上实现自己的消息传递是很常见的。
对于长期记录:常见的消息格式是将消息编码为:
decode(Bin) when is_binary(Bin) ->
<<Len:32/integer, R/binary>> = Bin,
<<Payload:Len/binary, Remain/binary>>,
{msg, {Len, Payload}, Remaining}.
也就是说,消息是 4 个字节,表示一个 32 位 bigendian 整数,后跟有效负载,其中长度由整数给出。这种格式和其他类似的格式非常常见,Erlang 直接在 C 层中包含针对它的优化解析器。要访问这些,您可以通过inet/setops/2 在套接字上设置选项,在我们的例子中,我们设置{packet, 4}。然后我们可以通过在socket上设置{active, once}来获取消息,等待下一条消息。当它到达时,我们可以再次在套接字上{active, once} 以获取下一条消息,依此类推。 gen_tcp 的文档中有一个示例(erl -man gen_tcp,如果您正确安装了 Erlang 手册页)。
其他常见格式是 asn.1 甚至是 http headers(!)。
技巧
创建一个可以对消息格式进行编码和解码然后将数据发送到系统其余部分的独立进程通常是有益的。通常在 Erlang 中一个好的解决方案是尽可能快地对传入的数据进行解复用,并将数据发送到一个进程,然后该进程可以处理剩下的问题。