里面有一些不靠谱的东西。与它们交谈的最简单方法可能是只显示不同的版本:
-module(biu_server).
-export([start_server/0,start/0]).
start() ->
spawn(fun() -> start_server() end).
start_server() ->
Options = [list, {reuseaddr, true}],
{ok, Listen} = gen_tcp:listen(1337, Options),
par_connect(Listen).
par_connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
% The line below is a whole different universe of confusion for now.
% Get sequential stuff figured out first.
% spawn(fun() -> par_connect(Listen) end),
loop(Socket).
loop(Socket) ->
ok = inet:setopts(Socket, [{active, once}]),
receive
{tcp, Socket, Request} ->
ok = io:format("Received TCP message: ~tp~n", [Request]),
Response = io_lib:format("You said: ~tp\r\n", [Request]),
gen_tcp:send(Socket, Response),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Server socket closed~n");
quit ->
ok = io:format("Fine! I QUIT!~n");
Unexpected ->
ok = io:format("Unexpected message: ~tp~n", [Unexpected]),
loop(Socket)
end.
请注意,上面我注释掉了生成一个新进程来处理连接的调用。原始版本存在的问题之一是剩余的侦听器无法终止,因为它总是阻塞TCP 接受 - 永远。
那里还有一些套接字控制问题——最简单的解决方法是让每个侦听器产生下一个侦听器并继续处理其新获取的连接。另一种方法是做你已经做过的事情,但是需要管理一些细节才能使其顺利运行。(示例(不用担心,仍然很容易):https ://github.com/zxq9/erlmud/blob/master/erlmud-0.1/tcplistener.erl )。
我还在主循环中的接收中添加了两个新子句:一个让我们告诉进程从 Erlang 中杀死自己,另一个处理来自系统内部的意外消息。“Unexpected”子句之所以重要有两个原因:它告诉我们发生了什么(您通常不应该收到神秘消息,但它可能会发生并且并不总是您的错),它可以防止进程邮箱堆积无法匹配但无法管理的消息。
只是坚持上面的版本,这是我第一次会议期间发生的事情......
在 Erlang 外壳中:
1> c(biu_server).
{ok,biu_server}
2> {Pid, Ref} = spawn_monitor(biu_server, start_server, []).
{<0.40.0>,#Ref<0.0.2.89>}
Received TCP message: "foo\r\n"
Received TCP message: "bar\r\n"
Received TCP message: "Yay! It works!\r\n"
Server socket closed
3> flush().
Shell got {'DOWN',#Ref<0.0.2.89>,process,<0.40.0>,normal}
ok
4> f().
ok
在 telnet 会话中:
ceverett@changa:~/Code/erlang$ telnet localhost 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
foo
You said: "foo\r\n"
bar
You said: "bar\r\n"
Yay! It works!
You said: "Yay! It works!\r\n"
^]
telnet> quit
Connection closed.
正如我们所见,来自客户端的关闭连接按预期终止。shell 的进程正在监视 TCP 服务器进程,所以当我flush()
最后看到'DOWN'
监视器消息时,正如预期的那样——正常退出。
现在让我们做一个类似的会话,但我们将使用 Erlang 端的quit
消息:
5> {Pid, Ref} = spawn_monitor(biu_server, start_server, []).
{<0.43.0>,#Ref<0.0.2.103>}
Received TCP message: "Still works.\r\n"
6> Pid ! "Launch the missiles!".
Unexpected message: "Launch the missiles!"
"Launch the missiles!"
7> Pid ! quit.
Fine! I QUIT!
quit
8> flush().
Shell got {'DOWN',#Ref<0.0.2.103>,process,<0.43.0>,normal}
ok
在远程登录方面:
ceverett@changa:~/Code/erlang$ telnet localhost 1337
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Still works.
You said: "Still works.\r\n"
Connection closed by foreign host.
您注意到那里的“\r\n”。这来自客户端的原始消息——所有 telnet 消息都以“\r\n”结尾(所以这就是你通常拆分流的方式——这是我们尚未解决的主题;这段代码实际上是假装 TCP 像 UDP 数据报一样工作,但事实并非如此......)。服务器返回消息中的“\r\n”被 telnet 客户端正确解释并中断到下一行。
顺便说一句,编写 telnet(或数据报)游戏是探索Erlang 网络模块的一种非常愉快的方式。