erlang游戏开发tcp

  之前在开发游戏的时候我们采用smartfoxserver这个java开发的游戏引擎,这个引擎在开发回合制游戏方面速度还是不错的。但是面对客户日益增长的需求还是有些力不从心。比如集群,比如灾备,热切换,热更新,热降级,面对上10G的流量攻击,隐藏真实的游戏服务地址等等需求 。这些问题虽然通过java编程和某些方法都能解决。但是权衡起来成本和精力都感觉不值。于是我一直在寻找一种能解决替换smartfoxserver的方案。无意之中发现了erlang恰恰能够解决我上述问题。于是便引起了我对erlang的探究。游戏通信框架主要由 通信,协议,后台服务组成。游戏通信框架我们首先要解决的就是通信问题,所以我第一个探究的也就是tcp socket 通信。

  erlang socket 通信的模式

  1. 主动消息获取(非阻塞)优点 接受消息简单  缺点 当客户端消息远远大于服务器端处理消息的时候 服务器崩溃

2. 被动消息获取(阻塞)   优点 可以控制处理消息的速度防止流量攻击  缺点 接受消息稍微复杂 要代码处理 recv

  3. 混合消息获取  (半阻塞)优点 接受消息方便,可以防止流量攻击 是前两种模式的一个综合

  经过多种对比和实验了后我选择了 混合消息获取模式。为了对这种模式的探究,我结合上一篇rebar template 做了一个简单消息发送,接收,连接用户发送消息广播的简单服务器端。下面是我实现的过程。

  1. 使用rebar 创建项目 rebar create template=simple_game projectid=game_socket  

==> thinkpad (create)
Writing game_socket/README.md
Writing game_socket/Makefile
Writing game_socket/.gitignore
Writing game_socket/rebar.config
Writing game_socket/start-dev.sh
Writing game_socket/apps/game_socket/src/game_socket.app.src
Writing game_socket/apps/game_socket/src/reloader.erl
Writing game_socket/apps/game_socket/src/game_socket.erl
Writing game_socket/apps/game_socket/src/game_socket_app.erl
Writing game_socket/apps/game_socket/src/game_socket_sup.erl
Writing game_socket/apps/game_socket/src/game_socket_server.erl
Writing game_socket/deps/README.md
Writing game_socket/rel/files/vm.args
Writing game_socket/rel/reltool.config
Writing game_socket/rel/files/app.config
Writing game_socket/rel/files/erl
Writing game_socket/rel/files/nodetool
Writing game_socket/rel/files/game_socket

 创建好的目录树   

.
├── apps
│   └── game_socket
│       └── src
│           ├── game_socket_app.erl
│           ├── game_socket.app.src
│           ├── game_socket.erl
│           ├── game_socket_server.erl
│           ├── game_socket_sup.erl
│           └── reloader.erl
├── deps
│   └── README.md
├── erl_crash.dump
├── Makefile
├── README.md
├── rebar.config
├── rel
│   ├── files
│   │   ├── app.config
│   │   ├── erl
│   │   ├── game_socket
│   │   ├── nodetool
│   │   └── vm.args
│   └── reltool.config
└── start-dev.sh

2. 修改对应已经创建好的项目

  2.1 game_socket.app.src  添加依赖项目,mnesia

  2.2 改造 game_socket_app.erl start 方法为如下

  

-define(PORT,9933).

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
    Port = case application:get_env(game_socket, port) of
               {ok, P} -> P;
               undefined -> ?PORT
           end,
    ok = game_socket_store:init(),
    {ok, LSock} = gen_tcp:listen(Port, [{active, false}]),
    case game_socket_sup:start_link(LSock) of
        {ok, Pid} ->
            {ok,_Child} = game_socket_sup:start_child(),
            {ok, Pid};
        Other ->
            {error, Other}
    end.

在整个程序代码中要注意匹配确认已经启动成功,如果不匹配可能没有启动成功被忽略。折腾了几个小时血的教训。

  2.3 改造 game_socket_sup.erl  这个文件主要注意 start_child 以及监控方式 重启方式 init 注意init 参数 Socket 是提供给 给game_socket_server.erl 初始使用的。

-module(game_socket_sup).

-behaviour(supervisor).

%% API
-export([start_link/1,start_child/0]).

%% Supervisor callbacks
-export([init/1]).

%% ===================================================================
%% API functions
%% ===================================================================

start_link(Lsock) ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, [Lsock]).

start_child()->
    supervisor:start_child(?MODULE,[]).

%% ===================================================================
%% Supervisor callbacks
%% ===================================================================

init([Lsock]) ->
    Server = {game_socket_server, {game_socket_server, start_link, [Lsock]},
              temporary, brutal_kill, worker, [game_socket_server]},
    {ok, { {simple_one_for_one, 0, 1}, [Server]} }.

  2.4 改照 game_socket_server.erl  整个文件 组要关注 init handle_info handle_info terminate 几个方法即可

-module(game_socket_server).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
-define(DFALUTPORT,9933).

-record(state ,{lsoket,tick=0,socket}).

%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------

-export([start_link/1]).

%% ------------------------------------------------------------------
%% gen_server Function Exports
%% ------------------------------------------------------------------

-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

%% ------------------------------------------------------------------
%% API Function Definitions
%% ------------------------------------------------------------------

start_link(Lsock) ->
    gen_server:start_link(?MODULE,[Lsock],[]).

%% ------------------------------------------------------------------
%% gen_server Function Definitions
%% ------------------------------------------------------------------

init([Socket]) ->
    %% time out
    inet:setopts(Socket, [{active, once}]),
    {ok, #state{lsoket=Socket},0}.

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast({chat,Msg},#state{socket=Socket}=State)->
    gen_tcp:send(Socket,io_lib:fwrite("~p~n", [Msg])),
    {noreply,State};

handle_cast(_Msg, State) ->
    {noreply, State}.

%--------------------------------------------------------------------
%% @doc  recive tcp socket data
%% @spec
%% @end
%%--------------------------------------------------------------------

handle_info({tcp, Socket, RawData}, State) ->
    inet:setopts(Socket, [{active, once}]),
    %% gen_tcp:send(Socket, io_lib:fwrite("~p~n", [RawData])),
    lists:foreach(fun(Pid)->
              case Pid=:=self() of
                false->
                  gen_server:cast(Pid,{chat,RawData});
                  _ ->ok
                  end
          end,
          game_socket_store:lookall()),
    {noreply, State};

%--------------------------------------------------------------------
%% @doc  accept socket
%% @spec
%% @end
%%--------------------------------------------------------------------

handle_info(timeout, #state{lsoket = LSock} = State) ->
    {ok, Sock} = gen_tcp:accept(LSock),
    game_socket_store:insert(self(),Sock),
    timer:send_interval(1000,timertick),
    game_socket_sup:start_child(),
    {noreply, State#state{socket=Sock}};

handle_info(timertick, #state{socket = _Sock} = State) ->
    TickCount = State#state.tick,
   %% io:format("~p~n",[TickCount]),
   %%en_tcp:send(Sock,<<TickCount>>),
    {noreply,State#state{tick=TickCount+1}};

%--------------------------------------------------------------------
%% @doc soket close
%% @spec
%% @end
%%--------------------------------------------------------------------

handle_info({tcp_closed, _Socket}, State) ->
    {stop, normal, State};

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    game_socket_store:delete(self()),
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------

  2.5 添加 game_socket_store.erl 在整个数据的存放过程中可以选ets 但是我选着了mnesia 是为后续的集群打下基础,在整个读取写入删除过程没有采取事物处理主要有pid 唯一的考虑 和操作速度较快的理由。如果有上下文逻辑处理可以采用事物处理数据。 

%%%-------------------------------------------------------------------
%%% @author thinkpad <>
%%% @copyright (C) 2014, thinkpad
%%% @doc
%%%
%%% @end
%%% Created :  4 Jul 2014 by thinkpad <>
%%%-------------------------------------------------------------------
-module(game_socket_store).
-record(user_info,{pid,socket}).
%% API
-export([init/0,insert/2,lookall/0,delete/1]).

%%%===================================================================
%%% API
%%%===================================================================

%--------------------------------------------------------------------
%% @doc  init mnesia db
%% @spec
%% @end
%%--------------------------------------------------------------------

init()->
    db_init().
%--------------------------------------------------------------------
%% @doc insert pid to db
%% @spec
%% @end
%%--------------------------------------------------------------------

insert(Pid,Socket)->
    mnesia:dirty_write(#user_info{pid=Pid,socket=Socket}).
%--------------------------------------------------------------------
%% @doc  get all socket pid
%% @spec
%% @end
%%--------------------------------------------------------------------

lookall()->
    mnesia:dirty_select(user_info,[{#user_info{pid=‘$1‘,socket = ‘$2‘},[],[‘$1‘]}]).

%--------------------------------------------------------------------
%% @doc delete socket pid in db
%% @spec
%% @end
%%--------------------------------------------------------------------

delete(Pid)->
    mnesia:dirty_delete(user_info,Pid).
%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------

%%%===================================================================
%%% Internal functions
%%%===================================================================
db_init() ->
    delete_schema(),
    {atomic, ok} = mnesia:create_table(user_info,[{attributes, record_info(fields,user_info)}]),
    ok.

delete_schema() ->
    mnesia:stop(),
    mnesia:delete_schema([node()]),
    mnesia:start().

  3. 开发调试项目  ./start-dev.sh 启动调试项目 调试效果如下图

([email protected]127.0.0.1)10> [email protected]:~/game_socket$ ./start-dev.sh
==> game_socket (clean)
==> game_socket (compile)
Compiled src/game_socket_store.erl
==> rel (compile)
==> game_socket (compile)
Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V6.1  (abort with ^G)
([email protected]127.0.0.1)1>
=INFO REPORT==== 4-Jul-2014::11:41:21 ===
    application: mnesia
    exited: stopped
    type: temporary

([email protected]127.0.0.1)1> 

  4. 发布镜像打包 make consle 发布测试 到次一个erlang 基本项目就完成 稍加逻辑处理 就可以改为游戏服务器使用了 聊天 消息 发送 都有了

[email protected]:~/game_socket$ make console
rebar compile
==> game_socket (compile)
Compiled src/game_socket_app.erl
==> rel (compile)
==> game_socket (compile)
rebar generate -f
==> rel (generate)
WARN:  ‘generate‘ command does not apply to directory /home/thinkpad/game_socket
rel/game_socket/bin/game_socket console
Exec: /home/thinkpad/game_socket/rel/game_socket/erts-6.1/bin/erlexec -boot /home/thinkpad/game_socket/rel/game_socket/releases/0.1.0/game_socket -embedded -config /home/thinkpad/game_socket/rel/game_socket/etc/app.config -args_file /home/thinkpad/game_socket/rel/game_socket/etc/vm.args -- console
Root: /home/thinkpad/game_socket/rel/game_socket
Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:4:4] [async-threads:5] [hipe] [kernel-poll:true]

=INFO REPORT==== 4-Jul-2014::11:46:24 ===
    application: mnesia
    exited: stopped
    type: permanent
Eshell V6.1  (abort with ^G)

  

  

erlang游戏开发tcp

时间: 2024-10-11 17:54:48

erlang游戏开发tcp的相关文章

Erlang游戏开发-协议

Erlang游戏开发-协议 选择什么协议? 协议包含通讯协议和数据格式. 通讯协议 通讯协议目前常用的是:HTTP 和TCP .其有各自的特点根据游戏的特点而进行选择. HTTP HTTP比较成熟,使用极其广泛.具有丰富的基础软件和工具.对于简单的social game可以使用HTTP作为通讯协议.这类游戏对实时性要求不是很高,使用HTTP也很容易做到性能扩展,可以较好的满足需求.如果游戏前端使用HTML+js开发,那么只能使用HTTP了,需要较好的交互性和实时性时,只能使用HTTP长轮询来实现

游戏开发协议(转)

转自:http://erlangdisplay.iteye.com/blog/1012785 Erlang游戏开发-协议 选择什么协议? 协议包含通讯协议和数据格式. 通讯协议 通讯协议目前常用的是:HTTP 和TCP .其有各自的特点根据游戏的特点而进行选择. HTTP HTTP比较成熟,使用极其广泛.具有丰富的基础软件和工具. 对于简单的social game可以使用HTTP作为通讯协议. 这类游戏对实时性要求不是很高,使用HTTP也很容易做到性能扩展,可以较好的满足需求. 如果游戏前端使用

erlang 游戏服务器开发

http://blog.csdn.net/slmeng2002/article/details/5532771 最近关注erlang游戏服务器开发  erlang大牛写的游戏服务器值得参考 介绍本文以我的OpenPoker项目为例子,讲述了一个构建超强伸缩性的在线多游戏玩家系统.OpenPoker是一个超强多玩家纸牌服务器,具有容错.负载均衡和无限伸缩性等特性.源代码位于我的个人站点上,大概10,000行代码,其中1/3是测试代码. 在OpenPoker最终版本敲定之前我做了大量调研,我尝试了D

同一世界服务器架构--Erlang游戏服务器

Erlang最大的优点是方便,很多基础功能都已经集成到Erlang语言中.之前用C++写服务器的时候,管理TCP连接很繁琐,需要写一大堆代码来实现.底层的框架需要写很多代码实现,这样既浪费时间,又会有很多BUG.但是用Erlang就方便多了,底层的一切你都不需要考虑,你只需要考虑,服务器的架构以及业务逻辑.从此让你彻底从底层的泥潭中解脱.我从去年年底开始了解学习Erlang,到现在我已经彻底爱上了Erlang.好了,废话不多说,开始详细介绍下我设计的这个服务器架构吧. 首先看下整个架构的布局,如

C++游戏开发需要阅读的书籍

如果要自学游戏程序开发的话,可以看看下面的,呵呵. 游戏开发资料(PDF书都是中文版的,非英文,很多是本人自己扫描制作,从未网上发布过,所以独家啦):  1.Gamebryo 2.2游戏引擎(盛大.腾讯等公司制作网络游戏常用)+Gamebryo v2.2.1说明文档 2.游戏PDF书及其代码:  3D游戏编程.3D游戏编程大师技巧.Direct3D游戏编程入门教程第2版.DirectX角色扮演游戏编程  DirectX特效游戏程序设计.MFC windows程序设计第2版.MFC深入浅出.VC+

GuidelinesOfGameDevelopment游戏开发新手指引

# GuidelinesOfGameDevelopment Just give out some experience or directions on game development to green hands.分享经验或路线给新手们 正文:最新指引链接 游戏类型:手游.端游.页游.家用游戏(电视) 游戏相关职位: 客户端: 游戏逻辑(常见功能和特色功能开发),将用户体验做到极致,未来方向应该是主程.游戏制作人和游戏玩法创新.C#或Lua.js.我想说,让Lua滚出游戏界,算了,还是我退出

Unity3D游戏开发之网络游戏服务器架构设计培训(如何做一名好主程)

在我们初期学习Unity3D培训目标:让U3D初学者可以更快速的掌握U3D技术,自行制作修改素材,可以独立完成2D.3D小规模游戏及网页游戏开发.后面就应该朝着主程的方面前进 今天给大家讲一下如何做一个好的主程 入手 假如,我现在接手一个新项目,我的身份还是主程序.在下属人员一一到位之前,在和制作人以及主策划充分沟通后,我需要先独自思考以下问题: 1.服务器跑在什么样的操作系统环境下?2.采用哪几种语言开发?主要是什么?3.服务器和客户端以什么样的接口通讯?4.采用哪些第三方的类库? 除了技术背

上班的第四百六十八天——第一次参与游戏开发

今天是我参与游戏开发的第一天,用一个字形容吧,就是"累". 说实话,我明天已经不想去了.看着他们个个油光满面,连头发都是油的,身上还有股味道,就知道昨天晚上加班太晚,没时间洗澡,其实这个我是能忍的.但是看着他们个个都驼背,薄弱的身躯显得肚子特别大,好像被一座大山压了很久那样子,眼睛里补满血丝,主程更是咳到心口痛.作为程序员,应该是属于精英分子,但是他们的样子告诉我,这是农民工啊!我开始急躁了,我做下去是不是也会这样呢? 当然,这个只是其次.最主要还是工作内容,我应聘的是C++游戏服务器

MMORPG游戏开发入门(转)

原著:Radu Privantu 译者序:这是一篇讲解如何开发一款MMORPG的入门文章,作者本人也是一款游戏的开发者,文中的内容源于实践,有很高的参考价值.很多人都想拥有自 己的游戏,这篇文章对那些想自己开发游戏的人来说可能是一纸福音,也可能是一盆冷水.无论如何,开发游戏都不是一件简单的事情.以下是翻译正文: 文章的中心是如何起步开发你自己的大型多人在线角色扮演游戏( 原文:Massive Multiplayer Online Role Playing Games) (MMORPG)(译者注: