Erlang gen_tcp: семантика recv (Socket, Length) - PullRequest
1 голос
/ 23 сентября 2010

После прочтения этого ответа я хочу понять, относится ли то же самое к вызовам на 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).

Это правильно?

1 Ответ

3 голосов
/ 26 сентября 2010

Да (или Нет, подробности см. В комментариях).

Рассмотрим:

Оболочка 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
...

Оболочка 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>

Оболочка 1, продолжение:

...
{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}, чтобы определить, сколько байтов должно быть длиной пакета (в системах с прямым порядком байтов).

То же, что и раньше, но без извлечения длины явно (обратите внимание, длина пакета = 2 в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
...

В этом случае Эрланг удалит первые 2 байта, а recv/2 заблокирует до тех пор, пока ему не потребуется столько байтов.В этом случае длина чтения должна быть 0 в recv/2.

Оболочка 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>

Оболочка 1:

...
{ok,#Port<0.512>}
3> {ok, Data} = gen_tcp:recv(C, 0).
{ok,<<"Hi">>}

В этом случае я не указываю параметр {packet, N} в оболочке 2 просто для демонстрации идеи, но обычно это не 0. Если установлена ​​опция packet, то gen_tcp автоматически добавит / удалит столько байтов изpackage.

Если вы укажете пакет 0, тогда вы должны сделать recv/2 с длиной> = 0, и поведение будет таким же, как в C. Вы можете смоделировать неблокирующие приемы, дав короткий тайм-аутпри выполнении приема, и это вернет {error, timeout} в этом случае.

Подробнее об этом можно прочитать здесь: http://www.erlang.org/doc/man/gen_tcp.html http://www.erlang.org/doc/man/inet.html#setopts-2

Надеюсь, это все прояснит.

...