阅读this answer后,我想了解同样适用于gen_tcp:recv(Socket, Length)
的调用。我对文档的理解是,如果缓冲区中有超过Length
个字节,它们就会保留在那里;如果小于Length
个字节,则调用将阻塞,直到足够可用或连接关闭。
特别是,当数据包以2个字节为前缀,以小端顺序保存数据包长度时,这应该有效:
receive_packet(Socket) ->
{ok, <<Length:16/integer-little>>} = gen_tcp:recv(Socket, 2),
gen_tcp:recv(Socket, Length).
这是对的吗?
答案 0 :(得分:3)
是(或否,请参阅评论以获取详细信息)。
考虑:
Shell 1:
1> {ok, L} = gen_tcp:listen(8080, [binary, {packet, 0}, {active, false}]).
{ok,#Port<0.506>}
2> {ok, C} = gen_tcp:accept(L). %% Blocks
...
Shell 2:
1> {ok, S} = gen_tcp:connect("localhost", 8080, [binary, {packet, 0}]).
{ok,#Port<0.516>}
2> gen_tcp:send(S, <<0,2,72,105>>).
ok
3>
Shell 1 cont:
...
{ok,#Port<0.512>}
3> {ok, <<Len:16/integer>>} = gen_tcp:recv(C, 2).
{ok,<<0,2>>}
4> Len.
2
5> {ok, Data} = gen_tcp:recv(C, Len).
{ok,<<"Hi">>}
6>
但是,如果您只想确认行为,这将非常有用。实际上,您将更改{packet, N}
选项以定义应该是数据包长度的字节数(在big-endian系统上)。
与之前相同,但没有明确提取长度(注意shell 1中的数据包长度= 2):
Shell 1:
1> {ok, L} = gen_tcp:listen(8080, [binary, {packet, 2}, {active, false}]).
{ok,#Port<0.506>}
2> {ok, C} = gen_tcp:accept(L). %% Blocks
...
在这种情况下,Erlang将剥离前2个字节,recv/2
将阻塞,直到它需要的字节数为止。在这种情况下,读取长度必须在recv/2
中为0。
Shell 2:
1> {ok, S} = gen_tcp:connect("localhost", 8080, [binary, {packet, 0}]).
{ok,#Port<0.516>}
2> gen_tcp:send(S, <<0,2,72,105>>).
ok
3>
Shell 1:
...
{ok,#Port<0.512>}
3> {ok, Data} = gen_tcp:recv(C, 0).
{ok,<<"Hi">>}
在这种情况下,我没有在shell 2中指定{packet, N}
选项只是为了显示想法,但通常不是0.如果设置了packet
选项,则gen_tcp
将自动附加/剥离包中的许多字节。
如果您指定数据包0,那么您必须执行长度为&gt; = 0的recv/2
并且行为与C中的行为相同。您可以通过在执行时缩短时间来模拟非阻塞接收在这种情况下,接收和返回{error,timeout}。
有关详细信息,请参阅此处: http://www.erlang.org/doc/man/gen_tcp.html http://www.erlang.org/doc/man/inet.html#setopts-2
希望这可以解决问题。