根据Erlang的语言特点,Erlang创建进程就如同Java创建对象那样简单。而Erlang的OTP框架,可以理解为是Java的Spring框架。

刚入门Erlang的tcp通信,书上的写法是根据socket用gen_tcp:send和receive通信,到了OTP里用gen_server也是一样的原理,只不过在OTP框架下gen_server行为模式封装了一些方法使得写法更方便。

首先是一般的写法。server端监听来自client的tcp连接。

%% server.erl
start(Port) ->{ok, LSocket} = gen_tcp:listen(Port, [binary, {packet, 4}, {active, true}, {reuseaddr, true}]),do_accept(LSocket).do_accept(LSocket) ->{ok, Socket} = gen_tcp:accept(LSocket),io:format("Socket ~p connnected. ~n", [Socket]).
%% client.erl
start(Port) ->{ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {packet, 4}]).

连接后生成的socket用于后续的交互通信,服务端端通常写个loop函数,监听来自客户端请求。而这个loop函数应该给它新建一进程,使其不影响服务端的tcp监听,通常这么写。

%% server.erlPid = spawn(fun() -> loop(Socket) end),gen_tcp:controlling_process(Socket, Pid).

gen_tcp:controlling_process的作用,我的理解是给loop函数创建一进程后,将这个进程号和Socket绑定,作用就是将tcp消息转换成进程消息,使其能通过{tcp, Socket, Bin}自动匹配接收。

整个demo如下:

-module(server).-export([start/1]).start(Port) ->{ok, LSocket} = gen_tcp:listen(Port, [binary, {packet, 4}, {active, true}, {reuseaddr, true}]),do_accept(LSocket).do_accept(LSocket) ->{ok, Socket} = gen_tcp:accept(LSocket),io:format("Socket ~p connnected. ~n", [Socket]),Pid = spawn(fun() -> loop(Socket) end),gen_tcp:controlling_process(Socket, Pid),loop(Socket).loop(Socket) ->receive{tcp, Socket, Bin} ->Str = binary_to_term(Bin),io:format("Server get the msg : ~p ~n", [Str]),gen_tcp:send(Socket, term_to_binary(hi)),loop(Socket);{tcp_closed, Socket} ->io:format("Socket ~p disconnected ~n", [Socket])end.
-module(client).
-export([start/1, send/1]).start(Port) ->ets:new(tcpname, [set, public, named_table]),{ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {packet, 4}]),ets:insert(tcpname, {socket, Socket}),Pid = spawn(fun() -> loop(Socket) end),gen_tcp:controlling_process(Socket, Pid).send(Str) ->Socket = ets:lookup_element(tcpname, socket, 2),gen_tcp:send(Socket, term_to_binary(Str)).loop(Socket) ->receive{tcp, Socket, Bin} ->Str = binary_to_term(Bin),io:format("Client get the msg : ~p ~n", [Str]),loop(Socket)end.

效果如下:
服务端:

客户端:

在OTP框架中,gen_server做了封装,新建进程不用spawn而是用gen_server:start_link,而接收来自tcp的消息用handle_info加字段匹配进行接收即可。

根据gen_server:start_link的写法,第二个参数为Module,即在gen_server里新建的进程将是一个新的模块(新的erl文件)。

%% server.erl%%Pid = spawn(fun() -> handle_clients(Socket) end),{ok, PidA} = gen_server:start_link(server_recv, Socket, []),

这么做的结果就是,server_recv将会接收来自client的tcp消息,并且不用receive,直接用handle_info即可接收,直接用字段匹配接收tcp消息将使得开发效率得到很大的提升。

%% server_recv.erl
handle_info({tcp, Socket, Bin}, State) ->Msg = binary_to_term(Bin),io:format("~p ~n", [Msg]),{noreply, State};

而在gen_server里,State用来保存服务器的状态,那么你可以定义一个record,将tcp连接生成的Socket保存到State里,那么在整个服务器所有地方(handle_call、handle_cast、handle_info)里都能取出来用。

(客户端将Socket保存到状态后,在需要发送消息时可从状态里取出来用)

%% client.erl
-record(state, {socket})....init(Port) ->{ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {packet, 4}]),{ok, #state{socket = LSocket}}....handle_cast({login, Data}, State) ->Socket = State#state.socket,gen_tcp:send(Socket, term_to_binary(Data)),{noreply, State};

完整demo如下:
① 服务端server:

-module(server).
-behaviour(gen_server).
-export([start/1]).-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(TCP_OPTIONS, [binary, {packet, 4}, {active, true}, {reuseaddr, true}]).-record(state, {socket}).start(Port) ->gen_server:start_link({local, ?SERVER}, ?MODULE, Port, []).init(Port) ->{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),self() ! {to_accept, LSocket},{ok, #state{socket = LSocket}}.do_accept(LSocket) ->{ok, Socket} = gen_tcp:accept(LSocket),io:fwrite("Socket connected: ~w ~n", [Socket]),%%Pid = spawn(fun() -> handle_clients(Socket) end),{ok, Pid} = gen_server:start_link(server_recv, Socket, []),gen_tcp:controlling_process(Socket, Pid),do_accept(LSocket).handle_info({to_accept, LSocket}, State) ->do_accept(LSocket),{noreply, State}.handle_call(stop, _From, Tab) ->{stop, normal, stopped, Tab}.handle_cast(_Msg, State) ->  {noreply, State}.terminate(_Reason, _State) ->ok.
code_change(_OldVsn, State, _Extra) ->{ok, State}.

② 接收客户端消息的新进程server_recv:

-module(server_recv).
-behaviour(gen_server).-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(TCP_OPTIONS, [binary, {packet, 4}, {active, true}, {reuseaddr, true}]).-record(state, {socket}).init(Socket) ->{ok, #state{socket = Socket}}.handle_info({hi, Data}, State) ->Socket = State#state.socket,gen_tcp:send(Socket, term_to_binary(Data)),{noreply, State};handle_info({tcp, Socket, Bin}, State) ->Msg = binary_to_term(Bin),io:format("server get the msg : ~p ~n", [Msg]),Msg1 = hi,self() ! {hi, Msg1},   {noreply, State};handle_info({tcp_closed, Socket}, State) ->io:fwrite("Socket disconnected: ~w ~n", [Socket]),{noreply, State}.handle_call(stop, _From, Tab) ->{stop, normal, stopped, Tab}.handle_cast(_Msg, State) ->{noreply, State}.terminate(_Reason, _State) ->ok.
code_change(_OldVsn, State, _Extra) ->{ok, State}.

③ 客户端client:

-module(client).
-behaviour(gen_server).
-export([start/1, hello/0]).-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(SERVER, ?MODULE).-record(state, {socket}).start(Port) ->gen_server:start_link({local, ?SERVER}, ?MODULE, Port, []).init(Port) ->{ok, Socket} = gen_tcp:connect("localhost", Port, [binary, {packet, 4}]),{ok, #state{socket = Socket}}.hello() ->Data = hello,gen_server:cast(?MODULE, {hello, Data}).handle_info({tcp, Socket, Bin}, State) ->Msg = binary_to_term(Bin),io:format("client get the msg : ~p ~n", [Msg]),{noreply, State};handle_info({tcp_closed, Socket}, State) ->io:fwrite("Socket disconnected: ~w ~n", [Socket]),{noreply, State}.handle_cast({hello, Data}, State) ->Socket = State#state.socket,gen_tcp:send(Socket, term_to_binary(Data)),{noreply, State}.handle_call(stop, _From, State) ->{stop, normal, stopped, State}.terminate(_Reason, _State) ->ok.
code_change(_OldVsn, State, Extra) ->{ok, State}.

小白入门分享,如有错误,欢迎指正。如有帮助,欢迎点赞收藏~

Erlang 入门——从普通tcp到OTP框架通信相关推荐

  1. python使用socket实现协议TCP长连接框架

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 使用python实现协议中常见的TCP长连接框架." 分析多了协议就会发现,很多的应用,特别是游戏类和IM类应用,它们的协议会使用 ...

  2. 诺禾- tcp 网络效劳框架

    一个工业级.跨平台.轻量级的 tcp 网络效劳框架:gevent 作为公司的公共产品,经常有这样的需求:就是新建一个本地效劳,产品线作为客户端经过 tcp 接入本地效劳,来获取想要的业务才干. 与印象 ...

  3. erlang 入门练习

    最近一段时间工作之余学习了一下erlang,并做了一个多节点通信的小用例,首先说明此用例都是用最简单的语句写的,没有什么otp gen_server之类的,因为还没看到学到这章:1先说明服务器代码为b ...

  4. java socket发送定长报文_一个基于TCP协议的Socket通信实例

    原标题:一个基于TCP协议的Socket通信实例 1. 前言 一般接口对接多以http/https或webservice的方式,socket方式的对接比较少并且会有一些难度.正好前段时间完成了一个so ...

  5. ROS入门笔记(十三):分布式通信

    ROS入门笔记(十三):分布式通信 文章目录 01 如何实现分布式多机通信 1.1 设置IP地址,确保底层链路的连通 1.2 在从机端设置ROS_MASTER_URI,让从机找到ROS Master ...

  6. AngularJS入门心得1——directive和controller如何通信

    粗略地翻了一遍<JavaScript DOM编程艺术>,就以为可以接过AngularJS的一招半式,一个星期过去了,我发现自己还是Too Young,Too Simple!(刚打照面的时候 ...

  7. Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象

    一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字server.bind() #把地址绑定到套接字,网络地址加端口server.liste ...

  8. (转)API SOCKET基础(一) TCP建立连接并通信

    写这篇日志,并不是要记录令人眼前一亮的算法,只是为了本人健忘的脑袋做一点准备. 要进行网络通信编程,就要用到socket(套接字),下面以TCP为例展示如何利用socket通信. 要 进行socket ...

  9. 【Java从0到架构师】分布式框架通信核心基础 - 序列化(JDK、Protobuf)、远程过程调用 RMI

    分布式框架通信核心基础 序列化 JDK 的序列化 JDK 序列化的一些细节 Protobuf 序列化 Protobuf 环境搭建与操作 Protobuf 原理分析 实际数据传输 序列化技术选型 远程过 ...

最新文章

  1. 在Ubuntu上为Android系统编写Linux内核驱动程序
  2. 为什么用 windbg 看 !address 显示出的Free是128T 大小?
  3. 允许使用抽象类类型 isearchboxinfo 的对象_Java学习5-设计模式+抽象类/方法
  4. C语言 · 输出日历
  5. java怎么从数据库中查询_java – 从数据库中检索的实体与查询中的情况相同
  6. 关于UIAlertAction如何修改sheet上的字体颜色
  7. CAD打印adobe acrobat pro/DC 安装的PDF打印机闪退问题
  8. 如何快速搜索文件和文件内容
  9. sqli-labs(38-41)
  10. SQL2008使用with求余额表,流水账方式
  11. PHP微信公众号网页授权登录 扫码登录 获取用户基本信息
  12. 会员管理系统(一)--页面登陆与注册
  13. metasploit unleashed(Chinese Simplified Edition)-8
  14. stm32---RS485半双工通信
  15. 神经网络和深度神经网络,图神经网络和神经网络
  16. 第八次网页前端培训(JavaScript)
  17. 微软word如何插入页码_如何在Microsoft Word中使用页码
  18. 零基础学Flink:Window Watermark
  19. 《Kotlin从小白到大牛》第28章:项目实战1:开发PetStore宠物商店项目
  20. 超级计算机淘汰后,扎心了!超级计算机模拟1/8决赛,利物浦皇马等被淘汰

热门文章

  1. scrapy爬虫--苏宁图书
  2. window route 路由表
  3. 用python爬取冰冰B站千条评论,我发现了这些...
  4. 使用webpack-hot-middleware不能刷新的解决方法
  5. 计算机弹钢琴谱子没有加减乘除,学钢琴不是为了学会多少曲子,而是学会举一反三...
  6. 硬盘格式化[低级/高级/快速格式化等等]
  7. 关于玛瑙的美与灵性有趣的事实
  8. Nacos OpenAPI清单
  9. c语言素数环,回溯法——素数环C++实现
  10. 电脑软件经常出现程序未响应是什么原因?及4种解决方法总结