2010-11-17 17 views
5
ben this öğreticisindeki socket_server kullanıyorum

ve bir istemci ve sunucu için aşağıdaki kod:gen_tcp smushed mesajlar

Sunucu:

-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). 

Müşteri:

-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. 

echo_server:start() ve echo_client:start() numaralı çağrılarımı kullanıyorum Aynı sistemdeki fermal kabuklar, bu sırayla. Gördüğüm şey:

Sunucu düzgün çalışıyor gibi görünüyor.

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>. 

istemci oldukça tüm mesajları doğru almaz: Ne lo ağ trafiğini bakarsak

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 

, ben her sayı saymaya için güzel temiz PSH/ACK çiftlerini görüyorum. Yukarıda işaret ettiğim satır, tek bir iletide iki paket gösteriliyor: 7 ve 6. Ağ üzerinden iki ayrı TCP paketi olarak geldi. Herkesin neden birbirine lanetlendikleri ya da onları nasıl özümseyecekleri hakkında bir fikri var mı? onlar alıcı uçta "smushed" ediliyoruz Neden

+0

@Closers: Bu soru, 2010'dan. Dünden bir sorunun nasıl bir kopyası var? – nmichaels

+0

Diğer soru kanonik olarak seçildi. – bjb568

cevap

7

: TCP bir akış protokolü, ve gönderme için bir gereklilik yoktur Çünkü/recv geldikleri bile ağ paketlerinin (ile 1-1 yazışmalar var çağrıları tel üzerinden bu şekilde).

Nasıl "un-Smush" onları: böylece paket sınırları nerede bilmek gerek kalmadan akışından mesajlar çıkarabilir, bir mesaj sınırlayıcı içerecek şekilde TCP protokolü değiştirmek Ya; veya TCP yerine UDP kullanın.

+0

Vay, bu yeni. UDP'yi kullanmamayı tercih ederim, o zamandan beri TCP'nin kendim için yaptığı tüm güzel şeyleri yapmak zorundayım (siparişi teslim etmeyi garantilemek gibi). TCP'de bir mesaj sınırlayıcıyı nasıl alacağınız konusunda daha spesifik olabilir misiniz? Gen_tcp'de kullanabileceğim bir şey var mı, yoksa kendi başıma mı dönmem gerekiyor? – nmichaels

+0

@Nathon: Erlang'ı bilmiyorum, bu yüzden gen_tcp'nin yetenekleri ile konuşamıyorum. TCP katmanından okuduğunuz mesajlar için bir çeşit arabellek yönetmek zorunda kalacaksınız, daha sonra tam bir mesajın ne zaman kullanılabilir olduğunu tespit etmenin bir yolunu bulacaksınız. Mesajların hepsi aynıysa, neredeyse bitti! Mesaj lenth alanı olarak bir mesajın ilk birkaç baytını ayırmak da yaygındır. Ya da gönderme tarafındaki uygun değilse, veri akışınızda görünmeyen bir bayt dizisi tanımlayabilir, ardından tam bir mesajın ne zaman hazır olduğunu algılamak için o diziyi arayabilirsiniz. –

+4

Ahah! {Paket, N} seçeneği, paketin ilk N baytını bir uzunluk alanı olarak kullanır. Http://www.erlang.org/doc/man/inet.html#setopts-2 – nmichaels