gen_tcp 粉碎消息

发布于 2024-10-03 13:06:45 字数 5781 浏览 5 评论 0原文

我正在使用教程中的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() 按顺序来自同一系统上的不同 shell。这是我所看到的:

服务器似乎工作得很好。

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 数据包通过网络传输。任何人都知道为什么它们会被压在一起或如何取消压在一起?

I'm using socket_server from this tutorial and the following code for a client and server:

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

Client:

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

I'm invoking echo_server:start() and echo_client:start() from different shells on the same system, in that order. Here's what I see:

The server seems to work just fine.

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

The client doesn't quite get all the messages right:

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

If I look at the network traffic on lo, I see nice clean PSH/ACK pairs for each number counting down. The line I pointed at above shows two packets showing up in a single message: 7 and 6. Those came across the network as two separate TCP packets. Anyone have any idea why they're being smushed together or how to un-smush them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

假装不在乎 2024-10-10 13:06:45

为什么它们在接收端被“压制”:因为 TCP 是一个流协议,
并且发送/接收调用不需要与 1-1 对应
网络数据包(即使它们通过线路到达)。

如何“解除”它们:更改 TCP 协议以包含消息分隔符,
因此您可以从流中提取消息,而无需知道数据包在哪里
边界是;或者使用 UDP 代替 TCP。

Why they're being "smushed" at the receiving end: Because TCP is a streaming protocol,
and there's no requirement for send/recv calls to have a 1-1 correspondence with
network packets (even if they arrive that way over the wire).

How to "un-smush" them: Either change your TCP protocol to include a message delimiter,
so you can extract messages from the stream without needing to know where the packet
boundaries were; or use UDP instead of TCP.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文