Erlang gen_tcp: семантика recv(Socket, Length)
Прочитав этот ответ, я хочу понять, относится ли то же самое к звонкам 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 ответ
Да (или Нет, см. Комментарии для деталей).
Рассматривать:
Оболочка 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 в оболочке 1):
Оболочка 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
автоматически добавит / уберет столько байтов из пакета.
Если вы указываете пакет 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
Надеюсь, это прояснит ситуацию.