Erlang Socket訊息獲取模式主動 被動 混合

weixin_33872660發表於2016-04-06

1、主動訊息獲取(非阻塞)
第一個例子是以主動模式開啟socket,然後接受來自socket的資料:
{ok,Listen} = gen_tcp:listen(Port,[…,{active,true}…]),
{ok,Socket} = gen_tcp:accept(Listen), loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Data} -> … 輸出處理 …
{tcp_closed,Socket} -> …
end.
這個過程無法控制發到伺服器迴圈的訊息流,如果客戶端產生資料的速度大於伺服器消費資料的速度,系統就會收到洪水般地訊息-訊息緩衝區溢位,系統將會crash並表現怪異。
這種型別的伺服器叫做非阻塞伺服器,因為它無法阻塞客戶端。我們僅在信任客戶端的情況下才會使用非阻塞伺服器。

2 被動訊息獲取(阻塞)
在這一節,我們寫阻塞伺服器:伺服器以被動模式開啟socket,通過 {active,false} 選項。這個伺服器不會被危險的客戶端洪水襲擊。
伺服器迴圈中的程式碼呼叫 gen_tcp:recv 來接收資料。客戶端在伺服器呼叫 recv 之前會被阻塞。注意OS會對客戶端發來的資料做一下緩衝,以允許客戶端在伺服器呼叫 recv 之前仍然可以繼續傳送一小段資料。
{ok,Listen} = gen_tcp:listen(Port,[…,{active,false}…]),
{ok,Socket} = gen_tcp:accept(Listen), loop(Socket).
loop(Socket) ->
case gen_tcp:recv(Socket,N) of {ok,B} -> … 資料處理 … loop(Socket);
{error,closed} …
end.

3 混合訊息獲取(部分阻塞)
你可能認為把被動模式用到所有伺服器上都合適。不幸的是,當我們在被動模式時,我們只能等待來自於一個socket的資料。這對於需要等待多個socket來源資料的伺服器則不適用。
幸運的是我們可以用混合方式,既不是阻塞的也不是非阻塞的。我們以一次主動(active once)模式 {active,once} 開啟socket。在這個模式中,socket是主動的,但是隻能接收一條訊息。在控制程式發出一條訊息之後,他必須明確的呼叫 inet:setopts 以便讓socket恢復並接收下一條訊息。系統在這發生之前會一直阻塞。這是兩種世界的最好結合點。如下是程式碼:
{ok,Listen} = gen_tcp:listen(Port,[…,{active,once}…]),
{ok,Socket} = gen_tcp:accept(Listen), loop(Socket).
loop(Socket) ->
receive
{tcp,Socket,Data} -> … 資料處理 … %%準備好啟用下一條訊息時
inet:setopts(Socket,[{active,once}]),
loop(Socket);
{tcp_closed,Socket} -> …
end.
使用 {active,once} 選項,使用者可以實現高層次的資料流控制(有時叫交通管制),同時又防止了伺服器被過多的訊息洪水所淹沒。

相關文章