2015-09-05 52 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