之前在开发游戏的时候我们采用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,).

%% ===================================================================
%% 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/,start_child/]). %% Supervisor callbacks
-export([init/]). %% ===================================================================
%% 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, , }, [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,). -record(state ,{lsoket,tick=,socket}). %% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------ -export([start_link/]). %% ------------------------------------------------------------------
%% gen_server Function Exports
%% ------------------------------------------------------------------ -export([init/, handle_call/, handle_cast/, handle_info/,
terminate/, code_change/]). %% ------------------------------------------------------------------
%% 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},}. 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(,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+}}; %--------------------------------------------------------------------
%% @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) , thinkpad
%%% @doc
%%%
%%% @end
%%% Created : Jul by thinkpad <>
%%%-------------------------------------------------------------------
-module(game_socket_store).
-record(user_info,{pid,socket}).
%% API
-export([init/,insert/,lookall/,delete/]). %%%===================================================================
%%% 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 启动调试项目 调试效果如下图

(game_socket@127.0.0.1)> thinkpad@thinkpad:~/game_socket$ ./start-dev.sh
==> game_socket (clean)
==> game_socket (compile)
Compiled src/game_socket_store.erl
==> rel (compile)
==> game_socket (compile)
Erlang/OTP [erts-6.1] [source] [-bit] [smp::] [async-threads:] [hipe] [kernel-poll:false] Eshell V6. (abort with ^G)
(game_socket@127.0.0.1)>
=INFO REPORT==== -Jul-:::: ===
application: mnesia
exited: stopped
type: temporary (game_socket@127.0.0.1)>

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

thinkpad@thinkpad:~/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./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 [erts-6.1] [source] [-bit] [smp::] [async-threads:] [hipe] [kernel-poll:true] =INFO REPORT==== -Jul-:::: ===
application: mnesia
exited: stopped
type: permanent
Eshell V6. (abort with ^G)

  

  

erlang游戏开发tcp的更多相关文章

  1. Erlang游戏开发-协议

    Erlang游戏开发-协议 选择什么协议? 协议包含通讯协议和数据格式. 通讯协议 通讯协议目前常用的是:HTTP 和TCP .其有各自的特点根据游戏的特点而进行选择. HTTP HTTP比较成熟,使 ...

  2. erlang 游戏服务器开发

    http://blog.csdn.net/slmeng2002/article/details/5532771 最近关注erlang游戏服务器开发  erlang大牛写的游戏服务器值得参考 介绍本文以 ...

  3. MVC模式在游戏开发的应用

    原地址: http://www.cocoachina.com/gamedev/2012/1129/5212.html MVC是三个单词的缩写,分别为:模型(Model).视图(View)和控制Cont ...

  4. Erlang游戏服设计总结

    这主要是一年多来,个人从事Erlang游戏服开发中对一些事情的思考. 想到哪说到哪,没有条理可言. 欢迎讨论. 通常Erlang游戏服务的设计涉及到的东东包括如下: 任务系统 活动系统 公会系统 玩法 ...

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

    如果要自学游戏程序开发的话,可以看看下面的,呵呵. 游戏开发资料(PDF书都是中文版的,非英文,很多是本人自己扫描制作,从未网上发布过,所以独家啦):  1.Gamebryo 2.2游戏引擎(盛大.腾 ...

  6. Unity3D手机游戏开发

    <Unity3D手机游戏开发> 基本信息 作者: 金玺曾 出版社:清华大学出版社 ISBN:9787302325550 上架时间:2013-8-7 出版日期:2013 年8月 开本:16开 ...

  7. nodejs五子棋online游戏开发视频教程,客户端cocos creator js

    开发的游戏是五子棋online,网络版的,服务端部分和客户端部分都在这个教程里面,可以看一下目录! 服务器nodejs游戏开发教程 使用Nodejs开发网络服务器 游戏服务端 ,cocos creat ...

  8. WEB与游戏开发的一些区别

    WEB与游戏开发的一些区别 前言 ​ 最近由于在准备期末考,以及准备实习.其实都没好好写过博客,但今天由于个人身边的一些事,所以对做web和做游戏开发的区别做个记录,以下都是从网上搜索到的资料文章,感 ...

  9. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

随机推荐

  1. Windows窗体应用程序常用的几个类的属性、方法以及事件

    System.Diagnostics.Process 属性 public bool EnableRaisingEvents { get; set; }//获取或设置在进程终止时是否应激发 Exited ...

  2. DataStage系列教程 by Bluebreeze

    突发奇想,用了这么久的DataStage,想要写点东西祭奠那逝去的岁月.希望可以坚持一直写完. DataStage系列教程 (Change Capture) DataStage系列教程 (Pivot_ ...

  3. 使用@Named注解绑定多个实现(java,scala)

    版权申明:转载请注明出处. 文章来源:http://bigdataer.net/?p=330 排版乱?请移步原文获得更好的阅读体验 1.背景 最近项目中使用了guice作为依赖注入的框架.碰到一个如下 ...

  4. c++之迭代器失效

    1.首先从一到题目开始谈说起迭代器失效.有时我们很自然并且自信地 用下面方法删除vector元素: #include <iostream>#include <stdio.h># ...

  5. 【Python】“UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9”根因及解决方法

    背景 自动化测试调用HTMLTestRunner生成测试报告的时候,出现了编码错误,错误如题 原因 搜索了很多资料,得出的结论是python的str默认是ascii编码,和unicode编码冲突,就会 ...

  6. Uncaught SyntaxError: Unexpected end of input 突然报了这个错

    最后排查:把 return true 注掉好了,接着在打开注释,依然不报错.最后不报错了.0.0 ~~~

  7. List和数组的相互转化

    一.数组转化为list:Arrays.aslist(arr); public static void main(String[] args) { String[] arr={"apple&q ...

  8. 三十一 Python分布式爬虫打造搜索引擎Scrapy精讲—chrome谷歌浏览器无界面运行、scrapy-splash、splinter

    1.chrome谷歌浏览器无界面运行 chrome谷歌浏览器无界面运行,主要运行在Linux系统,windows系统下不支持 chrome谷歌浏览器无界面运行需要一个模块,pyvirtualdispl ...

  9. cookie、session、sessionStorage、localStorage

    Cookie cookie是存储在浏览器端,并且随浏览器的请求一起发送到服务器端的,它有一定的过期时间,到了过期时间自动会消失. 首次设置cookie时是由服务器端发送到浏览器端 ,之后每次浏览器发送 ...

  10. NVML查询显卡信息

    前段时间做英伟达硬解得时候,显卡总是莫名挂掉,后来发现是因为显卡温度过高掉了.这几天找到CUDA中有NVML工具可以查看显卡信息,nvidia-smi也是基于这个工具包. 使用的CUDA版本为CUDA ...