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} 選項,用戶可以實現高層次的數據流控制(有時叫交通管制),同時又防止了服務器被過多的消息洪水所淹沒。