Erlang gen_tcp:recv(Socket, Length) semantics
After reading this answer, I want to underst开发者_StackOverflow社区and if the same applies to the calls to gen_tcp:recv(Socket, Length)
. My understanding of the documentation is that this if more than Length
bytes are available in the buffer, they remain there; if there is less than Length
bytes, the call blocks until enough is available or connection closes.
In particular, this should work when packets are prefixed by 2 bytes holding packet length in little-endian order:
receive_packet(Socket) ->
{ok, <<Length:16/integer-little>>} = gen_tcp:recv(Socket, 2),
gen_tcp:recv(Socket, Length).
Is this correct?
Yes (or No, see comments for details).
Consider:
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>
However this is useful if you only want to confirm the behaviour. In reality you would change the {packet, N}
option to define how many bytes that should be the packet length (on big-endian systems).
Same as before but without extracting length explicitly (note packet length = 2 in shell 1):
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
...
In this case Erlang will strip the first 2 bytes and recv/2
will block until as many bytes it needs. In this case read-length must be 0 in recv/2
.
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">>}
In this case I don't specify the {packet, N}
option in shell 2 just to show the idea but normally it is not 0. If the packet
option is set then gen_tcp
will automatically append/strip that many bytes from the package.
If you specify packet 0 then you must do a recv/2
with a length >= 0 and the behaviour is the same as in C. You can simulate non-blocking receives by giving a short time out when doing the receive and this will return {error, timeout} in that case.
More on that can be read here: http://www.erlang.org/doc/man/gen_tcp.html http://www.erlang.org/doc/man/inet.html#setopts-2
Hope this clears things up.
精彩评论