in gen_tcp:recv(Socket, 0)
вы спрашиваете ядро: «Дайте мне все данные, которые доступны прямо сейчас в приемном буфере». Однако ядро также может дать вам меньше. Даже для довольно быстрой ссылки вы, вероятно, нажмете медленный старт на TCP-соединении, поэтому в начале вы не получите много данных.
Решение - сделать свою собственную буферизацию. Вам придется съесть данные из нижележащего сокета, пока у вас не будет достаточно для создания сообщения. Из-за этого бинарные протоколы довольно часто реализуют свой собственный тип обмена сообщениями поверх потока.
Для записи на более длительный срок. Обычным форматом сообщения является кодирование сообщения в виде:
decode(Bin) when is_binary(Bin) ->
<<Len:32/integer, R/binary>> = Bin,
<<Payload:Len/binary, Remain/binary>>,
{msg, {Len, Payload}, Remaining}.
То есть сообщения представляют собой 4 байта, представляющих 32-разрядное бигендовское целое число, за которым следует полезная нагрузка, где длина задается целым числом. Этот формат и другие подобные ему настолько распространены, что Erlang включает оптимизированные парсеры для него непосредственно в C-слое. Чтобы получить к ним доступ, вы устанавливаете опции для сокета через inet/setops/2
, в нашем случае мы устанавливаем {packet, 4}
. Затем мы можем получать сообщения, установив {active, once}
в сокет и ждать следующего сообщения. Когда он прибудет, мы можем {active, once}
снова в сокете получить следующее сообщение и так далее. В документации gen_tcp
есть пример (erl -man gen_tcp
, если у вас правильно установлены man-страницы Erlang).
Другими распространенными форматами являются asn.1 или даже заголовки http (!).
Tricks
Часто выгодно создавать отдельный процесс, который может кодировать и декодировать формат вашего сообщения, а затем отправлять данные в остальную часть системы. Обычно хорошим решением в Erlang является демультиплексирование входящих данных как можно быстрее и передача данных в процесс, который затем может решить остальную часть проблемы.