[Erlang] Проблема с gen_tcp
От: Mamut Швеция http://dmitriid.com
Дата: 19.10.07 07:39
Оценка:
Вопрос был задан на форуме: 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.


Помогите, а?


dmitriid.comGitHubLinkedIn
Re: [Erlang] Проблема с gen_tcp
От: Курилка Россия http://kirya.narod.ru/
Дата: 19.10.07 07:41
Оценка:
Здравствуйте, Mamut, Вы писали:

M>Вопрос был задан на форуме: http://erlang.dmitriid.com/forum/topic/13

[cut]

У меня всё руки не доходили посмотреть, что там, а вечером уезжаю на родину...
Имхо проще было в квесчнз спросить, там бы сразу ответили.
Re: [Erlang] Проблема с gen_tcp
От: Vinick Россия  
Дата: 19.10.07 22:07
Оценка: 30 (1)
Здравствуйте, 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.
...
Re[2]: [Erlang] Проблема с gen_tcp
От: Vinick Россия  
Дата: 19.10.07 22:29
Оценка: 30 (1)
Здравствуйте, 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.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.