服务器在收到请求后不继续

Server not continuing after receiving request

当我 运行 我的服务器和客户端时,服务器收到一个请求,但是当 protocol/3 方法被调用时,它就停止了。我已经检查了调试器,它只是在函数调用上着陆,但没有任何反应。目前典型的 运行 如下所示:

server:start().

S = client:start().
client:authenticate(S, "Anon").

此时它刚刚停止工作。请注意,我 运行 服务器和客户端在两个单独的 erlang shell 上。

服务器代码

start() ->
    io:format("Starting Server..."),
    Clients = dict:new(),
    Database = spawn_link(fun() -> database(Clients)end),
    case gen_tcp:listen(1992, [binary, {active, false}]) of
        {ok, ListeningSocket} ->
            io:format("ok~n"),
            accept(ListeningSocket, Database),
            {ok, Port} = inet:port(ListeningSocket);
        {error, Reason} ->
            io:format("Failed: ~s", [Reason]),
            {error, Reason}
    end.

database(Clients) ->
receive
    {get_client, From, User} ->
        case dict:find(User, Clients) of
            {ok, UserSocket} ->
                From ! {id, UserSocket};
            error ->
                From ! error
        end,
        database(Clients);
    {ins_client, From, Socket, User} ->
        case dict:find(User, Clients) of
            {ok, _} ->
                From ! error;
            error ->
                Updated = dict:store(User, Socket, Clients),
                From ! ok,
                database(Updated)
        end
end.

accept(ListeningSocket, Database) ->
    io:format("Accepting..."),
    case gen_tcp:accept(ListeningSocket) of
        {ok, Socket} ->
            io:format("ok~n"),
            Pid = spawn(fun() -> loop(Socket, Database)end),
            gen_tcp:controlling_process(Socket, Pid);
        {error, Reason} ->
            io:format("Failed: ~s", [Reason])
    end,
    accept(ListeningSocket, Database).

loop(Socket, Database) ->
    inet:setopts(Socket, [{active, once}]),
    io:format("Waiting for request~n"),
    receive
        {tcp, Socket, Data} ->
            Request = binary_to_term(Data),
            protocol(Socket, Request, Database),
            loop(Socket, Database);
        {tcp_closed, Socket} ->
            io:format("Socket ~s closed. ~n", [Socket]),
            gen_tcp:close(Socket)
    end.

stop(Socket) ->
    gen_tcp:close(Socket).

protocol(Socket, Request, Database) ->
case Request of
    {message, To, Message} ->
        io:format("Message received: for ~s ~s", [To, Message]),
        Database ! {get_client, self(), To},
        receive
            {id, Receiver} ->
                gen_tcp:send(Receiver, term_to_binary(Message)),
                io:format("Sent to ~s: ~s~n", [Receiver, Message]);
            error ->
                gen_tcp:send(Socket, term_to_binary("User not found"))
        end;
    {enter, User} ->
        io:format("Request: {Enter: ~s}", User),
        Database ! {ins_client, self(), User},
        receive
            ok ->
                gen_tcp:send(Socket, term_to_binary("Authenticated"));
            error ->
                gen_tcp:send(Socket, term_to_binary("Authentication failed"))
        end;
    Other ->
        io:format("This happened: ~s", [Other])
end.

客户代码

start() ->
    io:format("Client starting..."),
    case gen_tcp:connect({127,0,0,1}, 1992, [binary, {active, false}]) of
        {ok, Socket} ->
            io:format("ok.\n"),
            spawn(fun() -> loop(Socket)end),
            Socket;
        {error, Reason} ->
            io:format("Failed: ~s", [Reason]),
            {error, Reason}
    end.

loop(Socket) ->
    inet:setopts(Socket, [{active, once}]),
    io:format("Waiting for data...~n"),
    receive
        {tcp_closed, Socket} ->
            io:format("Server ended connection.~n"),
            disconnect(Socket);
        {tcp, Socket, Data} ->
            io:format("From Server:' ~s' ~n", [Data])
    end.

authenticate(Socket, Username) ->
    inet:setopts(Socket, [{active, once}]),
    Term = {enter, Username},
    Request = term_to_binary(Term),
    gen_tcp:send(Socket, Request).

send(Socket, To, Input) ->
    inet:setopts(Socket, [{active, once}]),
    Message = term_to_binary({message, To, Input}),
    gen_tcp:send(Socket, Message).

disconnect(Socket) ->
    gen_tcp:close(Socket).

对于任何格式错误,我很抱歉,我对此还是很陌生:)

挂起的原因是您发送给数据库进程的 ins_client 消息缺少 Socket 元素。数据库进程只是忽略您不正确的 ins_client 消息,将其留在消息队列中。

其他一些需要解决的问题:

  • 接受连接后,您的服务器会生成一个接收循环进程,然后将新套接字的控制权转移到该新进程。最好生成一个新的接受器并直接调用循环,因为这样就不需要更改新套接字的控制过程。

    accept(ListeningSocket, Database) ->
        io:format("Accepting..."),
        case gen_tcp:accept(ListeningSocket) of
            {ok, Socket} ->
                io:format("ok~n"),
                spawn(fun() -> accept(ListeningSocket, Database) end),
                loop(Socket, Database);
            {error, Reason} ->
                io:format("Failed: ~s", [Reason]),
                error(Reason)
        end.
    
  • 在您的服务器 protocol 函数中,您在 io:format 中尝试打印用户时出错:User 参数需要在一个列表。

  • 您的 protocol 函数是一个带有内部 case 语句的大函数。最好写成多个子句,每个子句对应一个预期消息:

    protocol(Socket, {message, To, Message}, Database) ->
        io:format("Message received: for ~s ~s", [To, Message]),
        ...;
    protocol(Socket, {enter, User}, Database) ->
        io:format("Request: {Enter: ~s}", [User]),
        ...;
    protocol(_Socket, Other, _Database) ->
        io:format("This happened: ~s", [Other]).
    
  • 您的客户的 loop 函数很奇怪,因为它不是循环。它更像是一个普通的接收函数,因此您可能需要重命名它。

  • start 函数调用 loop 是没有意义的,因为新产生的进程不是套接字的控制进程,所以它永远不会收到任何东西。

  • 您应该创建处理 {tcp, Socket, Data} return Dataloop 子句,以便其调用者可以处理 returned数据.

  • 如果您的客户的 start 函数 return 编辑 {ok, Socket} 而不是仅仅 Socket 会更好,因为这样会更容易供调用者区分成功与失败。