收到请求后服务器无法继续

时间:2015-11-24 12:39:55

标签: erlang client-server

当我运行我的服务器和客户端时,服务器会收到一个请求,但是当调用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).

对于任何格式错误,我很抱歉,我仍然很新:)

1 个答案:

答案 0 :(得分:2)

挂起的原因是您的数据库进程的ins_client消息缺少Socket元素。数据库进程只是忽略了错误的ins_client消息,将其留在消息队列中。

要解决的其他一些问题:

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

    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参数需要在列表中。< / p>

  • 您的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函数是奇数,因为它不是循环。它更像是一个简单的接收功能,所以你可能想要重命名它。

  • loop函数调用start是没有意义的,因为新生成的进程不是套接字的控制进程,因此它永远不会收到任何内容。

  • 您应该使loop子句处理{tcp, Socket, Data}返回Data,因此其调用者可以处理返回的数据。

  • 如果您的客户的start函数返回{ok, Socket}成功而不仅仅是Socket,那会更好,因为这样可以让调用者更容易区分成功和失败