views:

84

answers:

1

After reading this answer, I want to understand 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?

+1  A: 

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.

Mazen Harake
The document you cited says that the Length parameter is only meaningful when the socket is in raw mode. I conclude that your answer is incorrect and that the correct answer is 'no', as per the thread the OP cited and as per every other TCP API I have ever used.
EJP
I re-read the question and I might have misunderstood the question. The question seems more like "Do I have to do recv/2 until I get to the end of the stream" and the answer to that in Erlang is no if you use packet option other then 0. Is this correct interpretation of the question? I don't really see any other reason for me to be wrong please clarify.
Mazen Harake
"In reality you would change the {packet, N} option to define how many bytes that should be the packet length." Not in this case, because in my protocol packet length is little-endian, but `{packet, N}` takes length to be big-endian.
Alexey Romanov
It _is_, however, misleading to call `gen_tcp:recv(C, 0)` when `{packet, 2}` option is given.
Alexey Romanov
When reading a TCP stream, the read method can return any number from 1 to the buffer size. It can return zero in non-blocking mode. Otherwise it blocks until at least one byte is available. Then it copies whatever bytes are now available into the user's buffer up to its length. It doesn't make any further attempt to fill the buffer. The piece you quoted does not apply. You have to keep reading until you get EOS, and you have to take note of the actual return value.
EJP
@EJP In Erlang, if you specify that the first 1, 2 or 4 bytes will determine the length then `recv/2` will block until you get that length. If you don't specify a length you have to call `recv/2` until you get as many bytes as you expect. So in Alexey's case he wouldn't use a packet size and thus the behaviour would be as you describe. I thought this was what the question was about in the beginning. Anyway don't care for accepted answers much as long as the answers are useful to people searching for them. I'll edit the post one last time.
Mazen Harake