2015-09-05 71 views
1

的代码如下:模拟线程池由二郎实现的web服务器不工作

-module(rudy). 
-export([init/1,handler/1,request/1,reply/1, start/1, stop/0]). 

start(Port) -> 
    register(rudy, spawn(fun() -> 
    init(Port) end)). 

stop() -> 
    exit(whereis(rudy), "time to die"). 

init(Port) -> 
    Opt = [list, {active, false}, {reuseaddr, true}], 
    case gen_tcp:listen(Port, Opt) of   % opens a listening socket 
    {ok, Listen} -> 
     spawn_many(3,Listen), 
%%  handler(Listen), 
     ok; 
    {error, _Error} -> error 
    end. 

handler(Listen) -> 
    case gen_tcp:accept(Listen) of   % listen to the socket 
    {ok, Client} -> 
     request(Client), 
     gen_tcp:close(Client), 
     handler(Listen); 
    {error, _Error} -> error 
    end. 
%% gen_tcp:close(Listen).   % close the socket 

request(Client) -> 
    Recv = gen_tcp:recv(Client, 0), 
    case Recv of 
    {ok, Str} -> 
     Request = http:parse_request(Str), 
     Response = reply(Request), 
     gen_tcp:send(Client, Response); 
    {error, Error} -> 
     io:format("rudy: error: ~w~n", [Error]) 
    end, 
    gen_tcp:close(Client). 

reply({{get, URI, _}, _, _}) -> 
    timer:sleep(40), 
    http:ok(URI). 

spawn_many(0, _Listen)-> ok; 
spawn_many(N, Listen)-> 
    spawn(rudy,handler,[Listen]), 
    spawn_many(N - 1, Listen). 

我打算创建3监听套接字的客户端连接到,但是这个代码没有按” t执行rudy:start(8027).,然后从网络浏览器访问http://localhost:8027/

罪魁祸首究竟在哪里?非常感谢。

回答

4

关于Erlang套接字的一件事是,打开一个进程控制它;当该进程死亡时,运行时将关闭套接字。

考虑您的start/1功能:

start(Port) -> 
    register(rudy, spawn(fun() -> 
    init(Port) end)). 

它衍生init/1功能,它会注册一个名称,然后返回。这意味着init/1在新的进程中运行,所以让我们来看看init/1

init(Port) -> 
    Opt = [list, {active, false}, {reuseaddr, true}], 
    case gen_tcp:listen(Port, Opt) of   % opens a listening socket 
    {ok, Listen} -> 
     spawn_many(3,Listen), 
%%  handler(Listen), 
     ok; 
    {error, _Error} -> error 
    end. 

运行init/1首先调用gen_tcp:listen/2将新生成的进程。如果成功,则调用spawn_many/2来设置一些接受者;如果失败,它基本上会忽略错误。但是这里是解决问题的关键:无论成功或失败,init/1都会结束,因此产生的进程也是如此,并且因为此进程(即侦听套接字的控制进程死亡),侦听套接字已关闭。任何尝试使用该套接字的接受者都会因此而失败,如果要打印出handler/1函数中的错误条件,则会看到该接受者。

解决此问题的方法是使init/1进程等待,直到使用侦听套接字的所有进程都关闭。要做到这一点

的一种方法是使init/1其PID传递到spawn_many(因此从spawn_many/2改变它spawn_many/3),具有init/1等待3个消息退出之前,并改变handler/1handler/2,服用PID作为额外的参数并在完成时发送消息。个最简单的方法有init/1等待所有的消息是有它调用递归函数像下面这样:

init(Port) -> 
    Opt = [list, {active, false}, {reuseaddr, true}], 
    case gen_tcp:listen(Port, Opt) of   % opens a listening socket 
    {ok, Listen} -> 
     Count = 3, 
     spawn_many(Count,Listen,self()), 
     wait_for_threads(Count); 
     %%  handler(Listen), 
    {error, _Error} -> 
     error 
    end. 

wait_for_threads(0) -> 
    ok; 
wait_for_threads(Count) -> 
    receive 
    handler_done -> 
     wait_for_threads(Count-1) 
    end. 

然后换handler/1handler/2,并将它发送消息:

handler(Listen, Pid) -> 
    case gen_tcp:accept(Listen) of   % listen to the socket 
    {ok, Client} -> 
     request(Client), 
     gen_tcp:close(Client), 
     handler(Listen, Pid); 
    {error, _Error} -> 
     error 
    end, 
    Pid ! handler_done. 

不要忘了接受额外的PID参数spawn_many/3

spawn_many(0, _Listen, _Pid)-> ok; 
spawn_many(N, Listen, Pid)-> 
    spawn(rudy,handler,[Listen, Pid]), 
    spawn_many(N - 1, Listen, Pid). 

所有这一切都足以记对于所有派生的接受者来说,监听套接字是活着的。

+0

再次感谢您,我是Erlang的新手,您的详细解答非常有帮助:] – Judking