我正在使用本教程中的socket_server 以及客户端和服务器的以下代码:
服务器:
-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).
客户:
-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()
和echo_client:start()
来自同一系统的不同的贝壳,在这个顺序.这是我看到的:
服务器似乎工作得很好.
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>.
客户端并没有完全正确地获取所有消息:
2> echo_client:start(). {ok,[{matched,nonode@nohost,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
如果我查看网络流量lo
,我会看到每个倒数的数字都很干净的PSH/ACK对.我在上面指出的行显示了在单个消息中出现的两个数据包:7和6.这些数据包作为两个单独的TCP数据包来到网络.任何人都知道他们为什么要一起被压榨或如何解开他们?
为什么他们在接收端被"扼杀":因为TCP是一种流媒体协议,并且不要求发送/接收呼叫与网络数据包具有1-1对应关系(即使它们通过网络到达) .
如何"取消屏蔽"它们:更改TCP协议以包含消息分隔符,这样您就可以从流中提取消息而无需知道数据包边界的位置; 或使用UDP而不是TCP.