zoukankan      html  css  js  c++  java
  • ranch实现游戏服务器

      在 erlang游戏开发tcp 我们建立起了自己的socket tcp 服务器的基本骨架。当时面对并发情况下,多人同一时刻连接服务器的时候,我们的基本骨架 还是难以应付处理。这就使我不得不想对这样的情况如何去处理。怎么处理呢? 预先开多个线程侦听连接,侦听到有连接后重新开线程处理当前连接,然后继续侦听连接。当然要自己处理这样的事情,也花费不到多少的时间。但是要弄成成熟稳定的骨架估计还是要花费一段时间的。本着不重复造轮子的原则。在这里我找到了大名鼎鼎cowboy使用的ranch。 它已经相当完美帮我实现了我前面所说的功能,并且经过大量的考验。下面我们就使用ranch 还实现我们的tcp 游戏服务器。

      ranch的使用模式分为两种:第一种独立运行模式;第二种嵌入模式。 cowboy使用的独立运行模式,程序自带的例子也是独立模式运行。在这里我要做的是在ranch嵌入到我们的游戏服务器中,作为我们游戏的一部分运行,在同一监督树下工作。

      整个实现过程在 erlang游戏开发tcp 基础上改造完成。

      1.在rebar.config 中添加对应的依赖项  

      
    {deps, [
          {ranch, ".*", {git, "https://github.com/extend/ranch.git", "master"}}
           ]}.
    View Code

      2.game_socket_app.erl修改为

      
    -module(game_socket_app).
    
    -behaviour(application).
    
    %% Application callbacks
    -export([start/2, stop/1]).
    
    -define(PORT,9933).
    -define(LISTEMNUM,5).
    
    %% ===================================================================
    %% Application callbacks
    %% ===================================================================
    
    start(_StartType, _StartArgs) ->
        %%读取启动端口
        Port = case application:get_env(game_socket, port) of
                   {ok, P} -> P;
                   undefined -> ?PORT
               end,
        %%侦听线程的个数
        ListenNum = case application:get_env(game_socket,listemnum) of
            {ok,L}->L;
             undefined->?LISTEMNUM
            end, 
        ok = game_socket_store:init(),
        %%启动监督树
        case game_socket_sup:start_link([Port,ListenNum]) of
            {ok, Pid} ->
                {ok, Pid};
            Other ->
                {error, Other}
        end.
    
    stop(_State) ->
        ok.
    View Code

      3.修改game_socket_sup.erl监督树  

      
    -module(game_socket_sup).
    
    -behaviour(supervisor).
    -define(CHILD(I, Type,Parms), {I, {I, start_link,Parms}, permanent, 5000, Type, [I]}).
    %% ===================================================================
    %% API functions
    %% ===================================================================
    %% API.
    -export([start_link/1]).
    
    %% supervisor.
    -export([init/1]).
    
    %% API.
    start_link([Port,ListenNum]) ->
        supervisor:start_link({local, ?MODULE}, ?MODULE, [Port,ListenNum]).
    %--------------------------------------------------------------------
    %% @doc init 
    %% @spec 
    %% @end
    %%--------------------------------------------------------------------
    
    init([Port,ListenNum]) ->
         %%启动ranch监督树
         RanchSpec=?CHILD(ranch_sup,supervisor,[]),    
         ListenerSpec = ranch:child_spec(game_socket_server,ListenNum,ranch_tcp, [{port, Port}], game_socket_server, []),
         Childs=[RanchSpec,ListenerSpec],
        {ok, {{one_for_one, 10, 10}, Childs}}.
    View Code

      4改造game_socket_server.erl  

      
    -module(game_socket_server).
    -behaviour(gen_server).
    -behaviour(ranch_protocol).
    
    %% API.
    -export([start_link/4]).
    
    %% gen_server.
    -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
             terminate/2, code_change/3]).
    
    -define(TIMEOUT, 500000).
    
    -define(SERVER, ?MODULE).
    
    -record(state, {ref,socket, transport,otp,ip,port}).
    
    %% API.
    
    start_link(Ref, Socket, Transport, Opts) ->
        gen_server:start_link(?MODULE, [Ref, Socket, Transport, Opts], []).
    
    %% gen_server.
    
    %% This function is never called. We only define it so that
    %% we can use the -behaviour(gen_server) attribute.
    
    init([Ref, Socket, Transport,Opts]) ->
        %%peername(Socket) -> {ok, {Address, Port}} | {error, posix()}      
        timer:send_interval(1000,timertick),
        {ok,{Address,Port}} = inet:peername(Socket),
        {ok, {state, Ref, Socket, Transport,Opts,Address,Port}, 0}.
    %% timout function set opt parms
    handle_info(timeout, State=#state{ref=Ref, socket=Socket, transport=Transport}) ->
        ok = ranch:accept_ack(Ref),
        ok = Transport:setopts(Socket, [{active, once}]),
        game_socket_store:insert(self(),Socket),
        {noreply, State};
    %% handle socket data 
    handle_info({tcp, Socket, Data}, State=#state{socket=Socket, transport=Transport}) ->
        Transport:setopts(Socket, [{active, once}]),
            io:format("~p~n",[Data]),
            lists:foreach(fun(Pid) ->
                      case Pid =:= self() of
                      false ->
                          gen_server:cast(Pid,{chat,Data});
                      true -> ok
                    end           
                  end,
                  game_socket_store:lookall()),
        {noreply, State, ?TIMEOUT};
    handle_info(timertick,State=#state{socket=Socket,transport=Transport})->
        Transport:send(Socket,<<1>>),
        {noreply,State};
    
    handle_info({tcp_closed, _Socket}, State) ->
        {stop, normal, State};
    handle_info({tcp_error, _, Reason}, State) ->
        {stop, Reason, State};
    handle_info(timeout, State) ->
        {stop, normal, State};
    handle_info(_Info, State) ->
        {stop, normal, State}.
    
    handle_call(_Request, _From, State) ->
             io:format("handle_call message ~p ~n",[_Request]),
        {reply, ok, State}.
    
    handle_cast({chat,Msg}, State=#state{socket=Socket, transport=Transport}) ->
        Transport:send(Socket,Msg),
        io:format("handle_cast message ~p ~n",[Msg]),
        {noreply, State}.
    
    terminate(_Reason, _State) ->
        game_socket_store:delete(self()),
        ok.
    
    code_change(_OldVsn, State, _Extra) ->
        {ok, State}.
    View Code

      经过以上4部我们初步的socket服务器搞定。怎么样感觉简单吧

      最后 rebar g-d   ./start-dev.sh  appmon:start().去看看监督树吧。然后在添加几个连接看看监督树。

  • 相关阅读:
    5 TensorFlow入门笔记之RNN实现手写数字识别
    用python简便地抓取刘昊然的写真(17行代码)
    MFC实现简单飞机大战(含游戏声音)
    4 TensorFlow入门之dropout解决overfitting问题
    3 TensorFlow入门之识别手写数字
    2 TensorFlow入门笔记之建造神经网络并将结果可视化
    1 TensorFlow入门笔记之基础架构
    用python的turtle画分形树
    win10下安装TensorFlow(CPU only)
    python中math常用函数
  • 原文地址:https://www.cnblogs.com/codew/p/3829129.html
Copyright © 2011-2022 走看看