英雄遠征Erlang源碼分析(4)-TCP連接處理

上一篇文章大概捋了一下游戲服務器啓動的時候對應模塊的動作,現在我們來仔細研究一下其中的start_tcp/0和start_client/0部分。

在start_tcp/0中啓動了sd_tcp_listener_sup監控樹,並掛到sd_sup下。其後啓動的進程樹形關係如下:

這是遊戲服務器啓動後使用observer觀察到的遊戲內進程關係圖,<0.68.0>爲start_tcp/0啓動的sd_tcp_listener_sup監控樹,<0.70.0>爲啓動的sd_tcp_listener進程。跟隨在sd_tcp_acceptor_sup後的十個進程爲sd_tcp_acceptor進程。下面結合代碼詳細解釋一下tcp連接的建立過程。

sd_tcp_listener:掛在sd_tcp_listener_sup監控樹下的gen_server,用於監聽tcp端口。看下它的初始化函數init/1:

init({AcceptorCount, Port}) ->
  process_flag(trap_exit, true),
  case gen_tcp:listen(Port, ?TCP_OPTIONS) of
    {ok, LSock} ->
      lists:foreach(fun (_) ->
        {ok, _APid} = supervisor:start_child(
          sd_tcp_acceptor_sup, [LSock])
                    end,
        lists:duplicate(AcceptorCount, dummy)),
      {ok, LSock};
    {error, Reason} ->
      {stop, {cannot_listen, Reason}}
  end.

使用gen_tcp:listen/2監聽指定的端口,建立ListenSocket後通過lists:duplicate/2創建10個sd_tcp_acceptor_sup的子進程(此處傳入的AcceptorCount爲10)。

sd_tcp_acceptor:掛在sd_tcp_acceptor_sup下的gen_server,在建立ListenSocket之後被創建。看一下它的初始化函數init/1:

init({LSock}) ->
    gen_server:cast(self(), accept),
    {ok, #state{sock=LSock}}.

進程在初始化的時候給自己cast了一條消息,內容爲原子accept,在hande_cast/2中接收到消息後調用accept/1函數,函數的實現:

accept(State = #state{sock=LSock}) ->
    case prim_inet:async_accept(LSock, -1) of
        {ok, Ref} -> {noreply, State#state{ref=Ref}};
        Error     -> {stop, {cannot_accept, Error}, State}
    end.

此處沒有使用gen_tcp:receive或者gen_tcp:accept來接收客戶端消息,而是使用了prim_inet:async_accept/2來進行異步的消息接收,如果有消息傳來,此時進程本身會收到一條格式爲{inet_async,S,Ref,Status}的消息,由於進程爲一個gen_server,我們在handle_info裏面處理:

handle_info({inet_async, LSock, Ref, {ok, Sock}}, State = #state{sock=LSock, ref=Ref}) ->
    case set_sockopt(LSock, Sock) of
        ok -> ok;
        {error, Reason} -> exit({set_sockopt, Reason})
    end,
    start_client(Sock),
    accept(State);

建立了與客戶端的Socket連接後,調用start_client/1函數:

start_client(Sock) ->
    {ok, Child} = supervisor:start_child(sd_tcp_client_sup, []),
    ok = gen_tcp:controlling_process(Sock, Child),
    Child ! {go, Sock}.

在sd_tcp_client_sup監控樹下新建客戶端進程sd_reader,使用gen_tcp:controlling_process/2將建立的Socket控制進程修改爲該客戶端進程,並給該進程發送一條{go,Socket}的消息,客戶端進程接收到這條消息後便開始接手與客戶端的通信。

之後sd_tcp_acceptor調用accept/1函數,進入循環等待客戶端連接的過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章