2010-11-17 13 views
5

Używam socket_server z this tutorialu i następujący kod dla klienta i serwera:gen_tcp smushed wiadomości

Serwer:

-module(echo_server). 
-export([start/0, loop/1]). 

% echo_server specific code 
start() -> 
    spawn(socket_server, start, [?MODULE, 7000, {?MODULE, loop}]). 

loop(Socket) -> 
    case gen_tcp:recv(Socket, 0) of 
     {ok, Message} -> 
      Msg = binary_to_term(Message), 
      case Msg of 
       start -> 
        io:format("Got start message on socket ~p.~n", [Socket]), 
        send_count(Socket, 10), 
        gen_tcp:close(Socket); 
       Other -> 
        io:format("Got message on socket ~p: ~p~n", 
           [Socket, Other]) 
      end; 
     {error, closed} -> 
      io:format("Got closed message on socket ~p.~n", [Socket]), 
      ok; 
     Error -> 
      io:format("Got bad message: ~p on socket ~p.~n", [Error, Socket]) 
    end. 

send_count(_Socket, 0) -> 
    ok; 
send_count(Socket, Num) -> 
    io:format("Sending ~p to ~p.~n", [Num, Socket]), 
    gen_tcp:send(Socket, term_to_binary(Num)), 
    send_count(Socket, Num - 1). 

Klient:

-module(echo_client). 
-export([start/0, do_stuff/0]). 


send(Socket, Msg) -> 
    gen_tcp:send(Socket, term_to_binary(Msg)). 

start() -> 
    dbg:tracer(), 
    Pid = spawn(?MODULE, do_stuff, []), 
    dbg:p(Pid, r). 

do_stuff() -> 
    case gen_tcp:connect("localhost", 7000, [binary, {packet, 0}]) of 
     {ok, Socket} -> 
      send(Socket, start), 
      rx_loop(Socket); 
     Error -> 
      io:format("Error connecting to server: ~p~n", [Error]) 
    end. 

rx_loop(Socket) -> 
    receive 
     {tcp, Socket, Message} -> 
      Msg = binary_to_term(Message), 
      io:format("Received message: ~p~n", [Msg]), 
      rx_loop(Socket) 
    after 5000 -> 
      finish_loop(Socket) 
    end. 

finish_loop(Socket) -> 
    receive 
     {tcp, Socket, Message} -> 
      Msg = binary_to_term(Message), 
      io:format("Received message: ~p~n", [Msg]), 
      rx_loop(Socket); 
     {tcp_closed, Socket} -> 
      io:format("Server terminated connection.~n"), 
      exit(normal); 
     Error -> 
      io:format("Received bad message: ~p~n", [Error]), 
      rx_loop(Socket) 
    end. 

Wywołuję echo_server:start() i echo_client:start() od di fοr muszle w tym samym systemie, w tej kolejności. Oto, co widzę:

Serwer wydaje się działać dobrze.

1>echo_server:start(). 
<0.39.0> 
Got start message on socket #Port<0.2041>. 
Sending 10 to #Port<0.2041>. 
Sending 9 to #Port<0.2041>. 
Sending 8 to #Port<0.2041>. 
Sending 7 to #Port<0.2041>. 
Sending 6 to #Port<0.2041>. 
Sending 5 to #Port<0.2041>. 
Sending 4 to #Port<0.2041>. 
Sending 3 to #Port<0.2041>. 
Sending 2 to #Port<0.2041>. 
Sending 1 to #Port<0.2041>. 

Klient nie dość uzyskać wszystkie komunikaty rację:

2> echo_client:start(). 
{ok,[{matched,[email protected],1}]} 
3> (<0.41.0>) << {code_server,{module,gen_tcp}} 
(<0.41.0>) << {code_server,{module,inet_tcp}} 
(<0.41.0>) << {#Ref<0.0.0.74>,{ok,<0.43.0>}} 
(<0.41.0>) << {#Ref<0.0.0.76>, 
       {ok,<<4,0,0,0,2,127,0,0,1,127,0,0,1,0,0,0,3,108,111,99,97,108, 
        104,111,115,116,0,105,112,54,45,108,111,99,97,108,104, 
        111,115,116,0,105,112,54,45,108,111,111,112,98,97,99, 
        107,0>>}} 
(<0.41.0>) << {inet_async,#Port<0.2058>,0,ok} 
(<0.41.0>) << {inet_reply,#Port<0.2058>,ok} 
Received message: 10 
3> (<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,10>>} 
Received message: 9 
3> (<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,9>>} 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
Received message: 8 
Received message: 5 
Received message: 4 
Received message: 3 
Received message: 2 
Received message: 1 
3> (<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,8,131,97,7,131,97,6>>} %% <---This guy here 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,5>>} 
(<0.41.0>) << timeout 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,4>>} 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,3>>} 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,2>>} 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 
(<0.41.0>) << {tcp,#Port<0.2058>,<<131,97,1>>} 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << {tcp_closed,#Port<0.2058>} 
(<0.41.0>) << timeout 
Server terminated connection. 
3> (<0.41.0>) << timeout 
(<0.41.0>) << {io_reply,<0.25.0>,ok} 
(<0.41.0>) << timeout 

Jeśli patrzę na ruch sieciowy na lo widzę ładne czyste pary PSH/ACK dla każdej liczby odliczanie. Linia wskazana powyżej pokazuje dwa pakiety pojawiające się w jednym komunikacie: 7 i 6. Te pojawiły się w sieci jako dwa oddzielne pakiety TCP. Ktoś ma jakiś pomysł, dlaczego są zrzucani razem lub jak je rozpraszać?

+0

@closers: To pytanie pochodzi z 2010 roku. Jak to jest powielanie pytania z wczoraj? – nmichaels

+0

Drugie pytanie zostało wybrane jako kanoniczne dup. – bjb568

Odpowiedz

7

Dlaczego oni są „smushed” w punkcie odbioru: Ponieważ TCP jest protokołem transmisji strumieniowej i nie ma wymogu send/recv nazywa mieć 1-1 korespondencję z pakietów sieciowych (nawet jeśli przylatują w ten sposób przez przewód).

Jak je rozpakować: Zmień protokół TCP tak, aby zawierał ogranicznik wiadomości, , aby można było pobierać wiadomości ze strumienia bez konieczności sprawdzania, gdzie były granice pakietów; lub użyj UDP zamiast TCP.

+0

Wow, to jest nowe. Wolałbym nie używać UDP, ponieważ wtedy musiałbym zrobić wszystkie fajne rzeczy, które robi sam TCP (jak zagwarantowanie dostarczania w kolejności). Czy możesz dokładniej określić, jak uzyskać separator wiadomości w TCP? Czy jest coś wbudowanego w gen_tcp, z którego mogę korzystać, czy też muszę przetworzyć własne? – nmichaels

+0

@Nathon: Nie znam Erlanga, więc nie mogę mówić o możliwościach gen_tcp. Prawie na pewno będziesz musiał zarządzać jakimś buforem dla wiadomości, które czytasz z warstwy TCP, a potem będziesz miał sposób na wykrycie, kiedy kompletna wiadomość jest dostępna. Jeśli twoje wiadomości są tej samej długości, prawie gotowe! Powszechne jest również rezerwowanie pierwszych kilku bajtów wiadomości jako pól danych wiadomości. Lub jeśli nie jest to wygodne po stronie wysyłającej, możesz zdefiniować sekwencję bajtów, która nie pojawi się w strumieniu danych, a następnie poszukaj tej sekwencji, aby wykryć, kiedy pełna wiadomość jest gotowa. –

+4

Ahah! Opcja {pakiet, N} używa pierwszych N bajtów pakietu jako pola długości. Istnieje długa lista opcji dla tego pola w http://www.erlang.org/doc/man/inet.html#setopts-2 – nmichaels

Powiązane problemy