Erlang 丢失消息

发布于 2024-10-02 10:25:52 字数 4296 浏览 7 评论 0原文

我正在使用 dbg:p(client, r) 运行以下代码:

-module(client).
-export([start/0, start/2, send/1, net_client/1]).

start() ->
    start("localhost", 7000).

start(Host, Port) ->
    io:format("Client connecting to ~p:~p.~n", [Host, Port]),
    register(ui, spawn(fun() -> gui_control([]) end)),
    case gen_tcp:connect(Host, Port, [binary, {packet, 0}]) of
        {ok, Socket} ->
            Pid = spawn(client, net_client, [Socket]),
            register(client, Pid),
            gen_tcp:controlling_process(Socket, Pid);
        Error ->
            io:format("Error connecting to server: ~p~n", [Error]),
            erlang:error("Could not connect to server.")
    end,
    ok.

send(Msg) ->
    client!{send, Msg}.


%% Forwards messages to either the GUI controller or the server.
net_client(Socket) ->
    receive
        {tcp, Socket, Message} ->
            Msg = binary_to_term(Message),
            io:format("Received TCP message on ~p: ~p~n", [Socket, Msg]),
            ui!{server, Msg};
        {send, Message} ->
            io:format("Sending ~p.~n", [Message]),
            gen_tcp:send(Socket, term_to_binary(Message));
        close ->
            gen_tcp:close(Socket),
            exit(normal);
        {tcp_closed, Socket} ->
            io:format("Server terminated connection.~n"),
            exit(normal); %% Reconnect?
        timeout -> %% This
            io:format("Timed out?~n");
        {inet_reply, Socket, Message} -> %% and this never happen.
            io:format("inet_reply: ~p~n", Message);
        Error ->
            io:format("Net client got bad message: ~p.~n", [Error])
    after 10000 ->
            refresh %% gen_tcp:send(Socket, term_to_binary(keepalive))
    end,
    ?MODULE:net_client(Socket).


gui_control(Data) ->
    receive
        %% This will hang the gui until the sync is done. Not sure if
        %% that's okay.
        {server, {sync, Datum}} -> % Resync command from server.
            gui_control(resync([Datum]));
        {client, refresh} -> % Refresh request from display.
            display:update(Data);
        {server, Msg} ->
            io:format("UI Rx: ~p~n", [Msg])
    end,
    gui_control(Data).

resync(Data) ->
    receive
        {server, {sync, Datum}} ->
            resync([Datum|Data]);
        {server, {done, Num}} ->
            case length(Data) of
                Num ->
                    Data;
                _ ->
                    io:format("Got done before all the data were received.~n"),
                    send({sync})
            end
    after 5000 ->
            io:format("Timed out waiting for data.~n"),
            send({sync})
    end.

它与我使用 gen_tcp 和 gen_server 编写的服务器进行通信,遵循 这个。我的主要问题是我无法可靠地收到所有消息。有时我会从调试输出中得到

(<0.2.0>) << {tcp,#Port<0.517>,
                  <<131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,107,110,
                    111,119,110,100,0,7,117,110,107,110,111,119,110,106,106,
                    104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,114,97,
                    119,97,2,131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,
                    107,110,111,119,110,100,0,7,117,110,107,110,111,119,110,
                    106,106,104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,
                    114,97,119,97,3,131,104,6,100,0,4,99,97,114,100,100,0,7,
                    117,110,107,110,111,119,110,100,0,7,117,110,107,110,111,
                    119,110,106,106,104,3,107,0,5,70,105,114,115,116,100,0,4,
                    100,114,97,119,97,0>>}

,但没有相应的 Received TCP message on #Port<0.517>:... 消息。我还会看到类似这样的内容:

(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}

但是 net_client 的接收中没有任何内容。我用wireshark观察了网络流量,我知道数据包正在到达它们应该去的地方并被ACKed。我做错了什么?

编辑:我使用 erl -smp enable -eval "client:start()." 调用此函数,以防万一。

I'm running the following code with dbg:p(client, r):

-module(client).
-export([start/0, start/2, send/1, net_client/1]).

start() ->
    start("localhost", 7000).

start(Host, Port) ->
    io:format("Client connecting to ~p:~p.~n", [Host, Port]),
    register(ui, spawn(fun() -> gui_control([]) end)),
    case gen_tcp:connect(Host, Port, [binary, {packet, 0}]) of
        {ok, Socket} ->
            Pid = spawn(client, net_client, [Socket]),
            register(client, Pid),
            gen_tcp:controlling_process(Socket, Pid);
        Error ->
            io:format("Error connecting to server: ~p~n", [Error]),
            erlang:error("Could not connect to server.")
    end,
    ok.

send(Msg) ->
    client!{send, Msg}.


%% Forwards messages to either the GUI controller or the server.
net_client(Socket) ->
    receive
        {tcp, Socket, Message} ->
            Msg = binary_to_term(Message),
            io:format("Received TCP message on ~p: ~p~n", [Socket, Msg]),
            ui!{server, Msg};
        {send, Message} ->
            io:format("Sending ~p.~n", [Message]),
            gen_tcp:send(Socket, term_to_binary(Message));
        close ->
            gen_tcp:close(Socket),
            exit(normal);
        {tcp_closed, Socket} ->
            io:format("Server terminated connection.~n"),
            exit(normal); %% Reconnect?
        timeout -> %% This
            io:format("Timed out?~n");
        {inet_reply, Socket, Message} -> %% and this never happen.
            io:format("inet_reply: ~p~n", Message);
        Error ->
            io:format("Net client got bad message: ~p.~n", [Error])
    after 10000 ->
            refresh %% gen_tcp:send(Socket, term_to_binary(keepalive))
    end,
    ?MODULE:net_client(Socket).


gui_control(Data) ->
    receive
        %% This will hang the gui until the sync is done. Not sure if
        %% that's okay.
        {server, {sync, Datum}} -> % Resync command from server.
            gui_control(resync([Datum]));
        {client, refresh} -> % Refresh request from display.
            display:update(Data);
        {server, Msg} ->
            io:format("UI Rx: ~p~n", [Msg])
    end,
    gui_control(Data).

resync(Data) ->
    receive
        {server, {sync, Datum}} ->
            resync([Datum|Data]);
        {server, {done, Num}} ->
            case length(Data) of
                Num ->
                    Data;
                _ ->
                    io:format("Got done before all the data were received.~n"),
                    send({sync})
            end
    after 5000 ->
            io:format("Timed out waiting for data.~n"),
            send({sync})
    end.

It communicates with a server I wrote with gen_tcp and gen_server, following this. My main problem is that I don't reliably receive all my messages. Sometimes I'll get

(<0.2.0>) << {tcp,#Port<0.517>,
                  <<131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,107,110,
                    111,119,110,100,0,7,117,110,107,110,111,119,110,106,106,
                    104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,114,97,
                    119,97,2,131,104,6,100,0,4,99,97,114,100,100,0,7,117,110,
                    107,110,111,119,110,100,0,7,117,110,107,110,111,119,110,
                    106,106,104,3,107,0,6,83,101,99,111,110,100,100,0,4,100,
                    114,97,119,97,3,131,104,6,100,0,4,99,97,114,100,100,0,7,
                    117,110,107,110,111,119,110,100,0,7,117,110,107,110,111,
                    119,110,106,106,104,3,107,0,5,70,105,114,115,116,100,0,4,
                    100,114,97,119,97,0>>}

from the debugging output, but no corresponding Received TCP message on #Port<0.517>:... message. I'll also see things like this:

(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}
(<0.2.0>) << timeout
(<0.2.0>) << {io_reply,<0.24.0>,ok}

but nothing from net_client's receive. I've watched the network traffic with wireshark and I know the packets are getting where they're supposed to go and being ACKed. What am I doing wrong?

Edit: I'm invoking this with erl -smp enable -eval "client:start()." in case it matters.

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

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

发布评论

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

评论(3

小鸟爱天空丶 2024-10-09 10:25:52

我想基本问题是“net_client”应该作为一个单独的进程产生。

在启动方法中,更改

register(client, self()),
net_client(Socket);

register(client, fun() -> net_client(Socket) end);

应该可以解决它。

I guess the basic problem is that 'net_client' should be spawned off as a separate process..

In the start method, change

register(client, self()),
net_client(Socket);

to

register(client, fun() -> net_client(Socket) end);

that should solve it..

宫墨修音 2024-10-09 10:25:52

另外,我建议使用 redbug(eper 的一部分)https://github.com/massemanet/eper追踪。它可以防止您的系统淹没在跟踪输出中,并提供极其简单的语法,例如:redbug:start("mymod:foo -> return", [{msgs,10}])。跟踪对 mymod:foo 的所有调用以及这些调用返回的内容,但给我的跟踪消息不超过 10 条。

Also, I recommend using redbug (part of eper) https://github.com/massemanet/eper when tracing. It protects you from drowning your system in trace output and provides a dead simple syntax, eg: redbug:start("mymod:foo -> return", [{msgs,10}]). trace all calls to mymod:foo and what those calls return but give me no more than 10 trace messages.

新人笑 2024-10-09 10:25:52

结果 {packet, 0} 是我的问题。将其替换为 {packet, 2} 一切正常。

Turns out {packet, 0} was my problem. Replace that with {packet, 2} and all is well.

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