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"}} ]}.
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.
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}}.
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}.
经过以上4部我们初步的socket服务器搞定。怎么样感觉简单吧
最后 rebar g-d ./start-dev.sh appmon:start().去看看监督树吧。然后在添加几个连接看看监督树。
posted on 2014-07-07 11:44 code.wang 阅读(1754) 评论(0) 编辑 收藏 举报