服务器在收到请求后不继续
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 Data
的 loop
子句,以便其调用者可以处理 returned数据.
如果您的客户的 start
函数 return 编辑 {ok, Socket}
而不是仅仅 Socket
会更好,因为这样会更容易供调用者区分成功与失败。
当我 运行 我的服务器和客户端时,服务器收到一个请求,但是当 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}
returnData
的loop
子句,以便其调用者可以处理 returned数据.如果您的客户的
start
函数 return 编辑{ok, Socket}
而不是仅仅Socket
会更好,因为这样会更容易供调用者区分成功与失败。