Вопрос был задан на форуме:
http://erlang.dmitriid.com/forum/topic/13
architect
Сделал небольшой тестовый пример:
-module(t).
-export([
s/0,
s/1,
t/1
]).
-define(SPORT, 54321).
s() ->
case gen_tcp:listen(?SPORT, [binary]) of
{ok, LS} ->
s(LS);
Err ->
io:format("Server listen error: ~p~n", [Err])
end.
s(LSock) ->
case gen_tcp:accept(LSock) of
{ok, Sock} ->
io:format("Accept connection: ~p~n", [Sock]),
spawn(?MODULE, s, [LSock]),
sl(Sock);
Err ->
io:format("Server accept error: ~p~n", [Err])
end.
sl(Sock) ->
receive
{tcp, Sock, Data} ->
Disp = binary_to_term(Data),
io:format("Receive data: ~p~n", [Disp]),
sl(Sock);
All ->
io:format("Catch undef data on server: ~p~n", [All]),
sl(Sock)
after 10000 ->
Ret = gen_tcp:close(Sock),
io:format("Connection ~p close.... ~p~n", [Sock, Ret])
end.
t(N) ->
case gen_tcp:connect("localhost", ?SPORT, [binary]) of
{ok, Sock} ->
msg(Sock, N);
Err ->
io:format("Client connect error: ~p~n", [Err])
end.
msg(_, 0) ->
io:format("All test done~n");
msg(S, N) ->
gen_tcp:send(S, term_to_binary("caramba")),
gen_tcp:send(S, term_to_binary(N)),
io:format("Message ~p send~n", [N]),
NN = N - 1,
msg(S, NN).
Тут s() запускеает сервак который слушает порт, устанавливает соединение и просто распечатывает принимаемые сообщения. t(N) это тестовый клиент — соединяется с серваком и отправляет ему N * 2 пакетов: в первом строка "caramba", а во втором номер сообщения. Проблемма в том что код не работает.
Запускаю в 2-х окнах на локальной машине.
Клиент:
Erlang (BEAM) emulator version 5.5.5 [async-threads:0]
Eshell V5.5.5 (abort with ^G)
1> t:t(10).
Message 10 send
Message 9 send
Message 8 send
Message 7 send
Message 6 send
Message 5 send
Message 4 send
Message 3 send
Message 2 send
Message 1 send
All test done
ok
2>
Сервер:
Erlang (BEAM) emulator version 5.5.5 [async-threads:0]
Eshell V5.5.5 (abort with ^G)
1> t:s().
Accept connection: #Port<0.98>
Receive data: "caramba"
Receive data: "caramba"
Receive data: "caramba"
Receive data: "caramba"
Receive data: "caramba"
Receive data: "caramba"
Receive data: "caramba"
Receive data: 2
Receive data: "caramba"
Connection #Port<0.98> close.... ok
ok
2>
Как видно сервер не принимает часть пакетов. Всего один раз он смог принять номер сообщения и только 8 раз из 10 успел принять строку. Хотя TCP гарантирует что пакеты придут в правильном порядке. Когда ставлю задержку между отправкой пакетов на клиенте — сервер начинает их правильно принимать. Что это? Баг в библиотеке gen_tcp или я что-то не так делаю? Можен настроить надо ещё что?
В опциях сокета стоит только binary.
Помогите, а?
Здравствуйте, Mamut, Вы писали:
M>Вопрос был задан на форуме: http://erlang.dmitriid.com/forum/topic/13
[cut]
У меня всё руки не доходили посмотреть, что там, а вечером уезжаю на родину...
Имхо проще было в квесчнз спросить, там бы сразу ответили.
Здравствуйте, Mamut, Вы писали:
M>Вопрос был задан на форуме: http://erlang.dmitriid.com/forum/topic/13
sl(Sock) ->
receive
{tcp, Sock, Data} ->
Disp = binary_to_term(Data),
io:format("Receive data: ~p~n", [Disp]),
протокол TCP передает поток байтов и не содержит никакой информации о границах пользовательских сообщений. В итоге вызов binary_to_term возвращает первый терм содержащийся в Data, а остаток принятых данных теряется. Вот рабочий вариант
...
s(LSock) ->
case gen_tcp:accept(LSock) of
{ok, Sock} ->
io:format("Accept connection: ~p~n", [Sock]),
spawn(?MODULE, s, [LSock]),
sl(Sock,<<>>);
Err ->
io:format("Server accept error: ~p~n", [Err])
end.
process_data(<<>>) -> <<>>;
process_data(B) ->
<<L:8,Data/binary>> = B,
if
size(Data) >= L ->
Disp = binary_to_term(Data),
io:format("Receive data: ~p~n", [Disp]),
<< Head:L/binary,Rest/binary>> = Data,
process_data(Rest);
true ->
B
end.
sl(Sock,Rest) ->
receive
{tcp, Sock, Data} ->
R = process_data(<<Rest/binary,Data/binary>>),
sl(Sock,R);
All ->
io:format("Catch undef data on server: ~p~n", [All]),
sl(Sock,<<>>)
after 10000 ->
Ret = gen_tcp:close(Sock),
io:format("Connection ~p close.... ~p~n", [Sock, Ret])
end.
t(N) ->
case gen_tcp:connect("localhost", ?SPORT, [binary,{packet,1}]) of % эта опция будет добавлять при каждом send
% длину отсылаемого пакета
{ok, Sock} ->
msg(Sock, N);
Err ->
io:format("Client connect error: ~p~n", [Err])
end.
...
Здравствуйте, Vinick, Вы писали:
Я тут документацию почитал — оказывается можно еще проще
....
s() ->
case gen_tcp:listen(?SPORT, [binary,{packet,1}]) of
{ok, LS} ->
s(LS);
Err ->
io:format("Server listen error: ~p~n", [Err])
end.
....
t(N) ->
case gen_tcp:connect("localhost", ?SPORT, [binary,{packet,1}]) of
{ok, Sock} ->
msg(Sock, N);
Err ->
io:format("Client connect error: ~p~n", [Err])
end.