http://blog.csdn.net/huang1196/article/details/38401197

Ranch:

简单来说,Ranch就是一个tcp acceptor pool,用于高并发下的tcp连接建立与管理。可以设置并发链接最大数量,在不关闭socket连接的情况下可以动态升级连接池。Cowboy就是使用的ranch。

https://github.com/ninenines/ranch

下面通过改造ranch自带的reverse example实现简易的服务端。

game_server.app.src

[plain]  view plain  copy
  1. {application, game_server, [
  2. {description, "Ranch TCP reverse example."},
  3. {vsn, "1"},
  4. {modules, []},
  5. {registered, []},
  6. {applications, [
  7. kernel,
  8. stdlib,
  9. ranch
  10. ]},
  11. {mod, {game_server_app, []}},
  12. {env, []}
  13. ]}.

game_server_app.erl

[plain]  view plain  copy
  1. -module(game_server_app).
  2. -behaviour(application).
  3. -export([start/2, stop/1]).
  4. %% start/2
  5. start(_Type, _StartArgs) ->
  6. {ok, _Pid} = ranch:start_listener(tcp_reverse, 1,
  7. ranch_tcp, [{port, 5555},{max_connections, 10240}], game_protocol, []),
  8. game_server_sup:start_link().
  9. %% stop/1
  10. stop(State) ->
  11. ok.

这里注意ranch:start_listener(Ref, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts) -> {ok, pid()} | {error, badarg}.

最大连接数max_connections就是在这里进行设定, 默认值1024. NbAcceptors, Acceptor的数量,具体数值要根据实际并发设置。

Ranch接受请求并建立连接,然后就会将具体的处理交给实现了ranch_protocol行为的game_protocol,erlang中的behaviour跟java中的接口差不多。

game_server_sup.erl

[plain]  view plain  copy
  1. -module(game_server_sup).
  2. -behaviour(supervisor).
  3. -export([start_link/0, init/1]).
  4. -spec start_link() -> {ok, pid()}.
  5. start_link() ->
  6. supervisor:start_link({local, ?MODULE}, ?MODULE, []).
  7. %% init/1
  8. init([]) ->
  9. {ok, {{one_for_one, 10, 10}, []}}.

game_protocol.erl

[plain]  view plain  copy
  1. -module(game_protocol).
  2. -behaviour(gen_server).
  3. -behaviour(ranch_protocol).
  4. %% API.
  5. -export([start_link/4]).
  6. %% gen_server.
  7. -export([init/4]).
  8. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
  9. -define(TIMEOUT, 50000).
  10. -record(state, {socket, transport}).
  11. %% API.
  12. start_link(Ref, Socket, Transport, Opts) ->
  13. proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
  14. %% gen_server.
  15. %% This function is never called. We only define it so that
  16. %% we can use the -behaviour(gen_server) attribute.
  17. init([]) -> {ok, undefined}.
  18. init(Ref, Socket, Transport, _Opts = []) ->
  19. ok = proc_lib:init_ack({ok, self()}),
  20. ok = ranch:accept_ack(Ref),
  21. ok = Transport:setopts(Socket, [{active, once}, {packet, 2}]),
  22. gen_server:enter_loop(?MODULE, [],
  23. #state{socket=Socket, transport=Transport},
  24. ?TIMEOUT).
  25. handle_info({tcp, Socket, Data}, State=#state{
  26. socket=Socket, transport=Transport}) ->
  27. io:format("Data:~p~n", [Data]),
  28. Transport:setopts(Socket, [{active, once}]),
  29. Transport:send(Socket, reverse_binary(Data)),
  30. {noreply, State, ?TIMEOUT};
  31. handle_info({tcp_closed, _Socket}, State) ->
  32. {stop, normal, State};
  33. handle_info({tcp_error, _, Reason}, State) ->
  34. {stop, Reason, State};
  35. handle_info(timeout, State) ->
  36. {stop, normal, State};
  37. handle_info(_Info, State) ->
  38. {stop, normal, State}.
  39. handle_call(_Request, _From, State) ->
  40. {reply, ok, State}.
  41. handle_cast(_Msg, State) ->
  42. {noreply, State}.
  43. terminate(_Reason, _State) ->
  44. ok.
  45. code_change(_OldVsn, State, _Extra) ->
  46. {ok, State}.
  47. %% Internal.
  48. reverse_binary(B) when is_binary(B) ->
  49. list_to_binary(lists:reverse(binary_to_list(B))).

这里init的实现与常规的gen_server不一样。首先来说为什么不能用常规的gen_server写法。常规写法如下:

[plain]  view plain  copy
  1. init([Ref, Socket, Transport, Opts]) ->
  2. ok = ranch:accept_ack(Ref),
  3. ok = Transport:setopts(Socket, [{active, once}, {packet, 2}]),
  4. {ok, #state{socket=Socket, transport=Transport}}.

gen_server的start_link只有在init/1执行完毕后才会返回,但我们来看ranch:accept_ack(Ref):

[plain]  view plain  copy
  1. -spec accept_ack(ref()) -> ok.
  2. accept_ack(Ref) ->
  3. receive {shoot, Ref, Transport, Socket, AckTimeout} ->
  4. Transport:accept_ack(Socket, AckTimeout)
  5. end.

运行ranch:accept_ack/1时,进程会阻塞,等待{shoot, ...}这条消息,直到接收到此消息才会继续执行,接着才会完成init。但是{shoot, ...}这条消息从哪里来?查下ranch源码不难发现,ranch在建立了与新的gen_server进程的连接后,会向gen_server进程发送该消息(参考ranch_conns_sup:loop/4). 显然,gen_server进程在等待ranch:accept_ack接收到{shoot,...}消息迟迟不能返回,而ranch又无法与gen_server进程连接发送不了{shoot, ...}消息,造成死锁。故使用proc_lib:start_link/3优雅地解决了此问题。

下面copy一下文档的一个说明:

By default the socket will be set to return `binary` data, with the
options `{active, false}`, `{packet, raw}`, `{reuseaddr, true}` set.
These values can't be overriden when starting the listener, but
they can be overriden using `Transport:setopts/2` in the protocol.

It will also set `{backlog, 1024}` and `{nodelay, true}`, which
can be overriden at listener startup.

这也就是为什么{active, once}, {packet, 2}只能在procotol里重写

这样就实现了一个基本的服务端,make后编写脚本启动:

start.sh

[plain]  view plain  copy
  1. erl -pa ebin deps/*/ebin +K true +P 199999 \
  2. -sname game_server \
  3. -s game

-s game表示启动时默认调用game:start/0方法。

game.erl

[plain]  view plain  copy
  1. -module(game).
  2. %% ====================================================================
  3. %% API functions
  4. %% ====================================================================
  5. -export([start/0, stop/0]).
  6. start() ->
  7. ok = application:start(ranch),
  8. ok = application:start(game_server).
  9. stop() ->
  10. application:stop(ranch),
  11. application:stop(game_server).

如果设置{packet, raw}的话,直接打开一个Terminal $ telnet localhost 5555 就可以进行测试了。

不过这里设置的{packet,2}, 所以写了个测试client发送消息,建立连接->发送消息->接收返回消息->关闭连接:

[plain]  view plain  copy
  1. -module(client).
  2. -export([send/1]).
  3. send(BinMsg) ->
  4. SomeHostInNet = "localhost",
  5. {ok, Sock} = gen_tcp:connect(SomeHostInNet, 5555,
  6. [binary, {packet, 2}]),
  7. ok = gen_tcp:send(Sock, BinMsg),
  8. receive
  9. {tcp,Socket,String} ->
  10. io:format("Client received = ~p~n",[String]),
  11. gen_tcp:close(Socket)
  12. after 60000 ->
  13. exit
  14. end,
  15. ok = gen_tcp:close(Sock).

handler_info中加入不同消息的处理,就可以时间一个简单的游戏服务器了。R17后可以使用{active, N}, 程序效率应该会更高。

release和项目下载见: http://blog.csdn.net/huang1196/article/details/41010127

使用erlang ranch tcp开发服务端相关推荐

  1. 易语言tcp多线程服务端客户端_从TCP协议到TCP通信的各种异常现象和分析

    很多人总觉得学习TCP/IP协议没什么用,觉得日常编程开发只需要知道socket接口怎么用就可以了.如果大家定位过线上问题就会知道,实际上并非如此.如果应用在局域网内,且设备一切正常的情况下可能确实如 ...

  2. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  3. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  4. java贪吃蛇客户端服务器_java Socket套接字TCP编程开发服务端和客户端之间的通信 - 贪吃蛇学院-专业IT技术平台...

    超级简单,没有太多实质内容的Socket服务端,客户端小程序 先运行server 再运行client OK 服务端代码如下: public class Server { public static v ...

  5. QT网络编程开发服务端

    下一篇: QT网络编程开发客户端 文章目录 基于Qt的网络编程服务端 QTcpServer 配置 listen() close() newConnection() SINGL readyRead() ...

  6. C#创建TCP/IP服务端和客户端,含测试demo及源码

    网上的TCP/IP创建服务端和客户端的方法鱼龙混杂,自己把在使用项目中的TCP服务端和客户端的代码抽了出来,做了demo,以供大家使用参考. 为了方便调用,我把一些基础方法都整合封装到了Reader. ...

  7. 从编程小白到全栈开发:基于框架开发服务端

    上文中,我们了解了关于服务器端的一些概念知识,尤其是HTTP协议相关的最基本知识点,今天我想跟大家分享一下在平时正真的开发中,是如何来利用和体现这些内容的. 还记得我在<从编程小白到全栈开发:改 ...

  8. java批量生成订单号_【笔记6-支付及订单模块】从0开始 独立完成企业级Java电商网站开发(服务端)...

    支付模块 实际开发工作中经常会遇见如下场景,一个支付模块,一个订单模块,有一定依赖,一个同事负责支付模块,另一个同事负责订单模块,但是开发支付模块的时候要依赖订单模块的相关类 ,方法,或者工具类,这些 ...

  9. 基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现

    设计概述 服务端通信组件的设计是一项非常严谨的工作,其中性能.伸缩性和稳定性是必须考虑的硬性质量指标,若要把组件设计为通用组件提供给多种已知或未知的上层应用使用,则设计的难度更会大大增加,通用性.可用 ...

最新文章

  1. day3 集合、文件操作、函数、局部变量
  2. Android初学第29天
  3. WIndows 下安装mysql (non-install版本,即绿色版,或称为 源码包)
  4. 网页实时聊天之PHP如何实现websocket
  5. 微型计算机的典型应用场景,单片机有哪些类型和应用场景?-MCU解决方案
  6. 五大关键物联网应用助力优化数据中心基础设施管理
  7. Spring容器与上下文理解
  8. [react] render函数中return如果没有使用()会有什么问题?
  9. WordPress架构简单剖析
  10. 输入python出现商店_Win 10 中使用 Python 碰到的奇怪现象
  11. win10 登录显示0x800704cf错误代码
  12. [html] 你有使用过blockquote标签吗?说说它的用途有哪些?
  13. 如何区分网线是几类的_如何判断网线是几类线?
  14. c语言程序设计第六章习题答案,C语言程序设计(第2版) 刘克威,张凌晓著 习题答案-第六章...
  15. iPhone无法连接Wi-Fi解决方法
  16. 华为P40系列手机camera特性分析
  17. Spring - bean
  18. java数值型转字符型_Java中数值型,字符型及字符串的相互转换
  19. Git(5) SourceTree安装使用
  20. 我的世界怎么在服务器中显示键位,我的世界神奇宝贝mod怎么玩 基本键位介绍...

热门文章

  1. 什么是repair?什么是soft repair、hard repair、lane repair?
  2. write tcp 127.0.0.1:53008->127.0.0.1:6379: use of closed network connection原因—Go连接Redis
  3. 关于机器学习中的朴素贝叶斯以及拉普拉斯平滑
  4. steam游戏搬砖项目,不错的副业,具体操作方法
  5. 阿尔法狗打脸啪啪响啊~
  6. 动效告白对象神器HTML源码+前端程序员的浪漫
  7. 知不知道什么叫米筐量化?怎么来的?
  8. Golang代码质量检查工具GolangCI-Lint(学习笔记)
  9. 物联网IoT:开源代码基于CNN的红外图像人检测夜间入侵预警系统
  10. 硬件服务器搭建系统步骤,服务器硬件部署方案