根据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. 脉冲20KV高压发生器电弧打火
  2. qemu debug linux内核,qemu调试linux内核
  3. rop的noejs客户端
  4. ***病毒的隐藏方式
  5. 计算机系统安全风险管理,信息系统安全风险及其控制措施.doc
  6. jquery入门与实践案例教程
  7. 前端学习(175):弹窗
  8. docker更新容器命令 ,自启
  9. ddos攻击工具_linux下DDoS攻击模拟实战
  10. 本地计算机用plsql访问虚拟机中的oracle数据库
  11. linux加protobuf变量环境,protobuf简单介绍和ubuntu 16.04环境下安装教程
  12. node.js入门及安装
  13. 读书笔记之文件和注册表操作
  14. web安全day17:天天都在说的中间人攻击到底是啥
  15. MATLAB 2016a 安装包以及安装破解教程
  16. sql2000 sp3、sql2000 sp4升级补丁下载和安装须知:
  17. Mac版哔哩哔哩视频下载工具
  18. h5 监听安卓物理返回键
  19. Typora配置smms图床
  20. 从Go走进plan9汇编

热门文章

  1. linux i3 桌面,i3 窗口管理器,使 Linux 更美好
  2. 国美通讯:没有获悉任何有关黄光裕出狱的相关信息
  3. 《程序员》2012年3期精彩内容:互联网安全
  4. tomcat设置context不生效_tomcat部署应用时设置context path为空的上下文路径问题
  5. 一些对数学领域及数学研究的个人看法(转载自博士论坛wcboy)
  6. python怎么实现自动化运维_如何做好python自动化运维
  7. 【Android病毒分析报告】 - KorBankDemon “吸金幽灵”打劫银行
  8. eclipse 中Java导入外部项目报Description Type Target runtime Apache Tomcat v8.5 is not defined.
  9. UI设计师常去的网站
  10. WSL2迁移系统盘的docker-desktop和docker-desktop-data到其他盘