- 浏览: 337733 次
- 性别:
- 来自: 北京
最新评论
-
perfect_control:
真的很详细,一些东西很容易被我忽略掉了
使用fprof进行性能分析 -
leeyisoft:
http://www.erlangqa.com/ 怎么变成 “ ...
Erlang问答网站,欢迎各位提出问题,解答问题。 -
simsunny22:
4年之后我才看到 慢慢的干货
Erlang服务器内存耗尽bug跟踪过程 -
爱死我:
...
使用etop查看系统中进程信息 -
宋兵甲:
在跑这个服务的时候,每秒建立一个客户端连接,连续建立10000 ...
自己写一个tcp 通用服务器
我们想写这样一个tcp server,其绑定本地某个端口,用户可以接入实现特定的业务,比如一个傻傻的echo server,一个帮助服务器等等。。毫无疑问这个tcp的框架是相同的,想想我们一直以来怎么写tcp server:
创建socket -> 绑定端口 -> listen监听 -> accept tcp 连接 -> 处理业务 -> 关闭连接。中间可能会有多线程或者线程池等等不同的实现方式。
在erlang的世界,我们还是需要绑定端口,接受连接,处理业务,关闭连接,但是我们没有什么线程,锁的烦恼。我们为每个连接建立一个process,处理业务。因为erlang的process轻量,高效,成千上万。
我们用gen_sever behaviour来实现这个通用的tcp server,gen_server其实内部包含一个大循环,如果我们的tcp server再gen_server的循环中调用gen_tcp:accept/1,那么我们会阻塞gen_server,这肯定行不通。那么只有将 gen_tcp:accept放到一个独立的process中了。
我们的generic_server中定义了一个record:
这个record用来记录服务器的相关信息,注释已经相当清楚。
接下来让我们实现gen_server的init/1函数(如果对gen_server不熟悉,请参看以前文章,或者erlang官方文档),这个部分非常关键:
创建listen 端口成功时,我们调用accept/1函数:
accept_loop照此实现:
看明白了么?
我们就是在accept里生成一个新的process,其执行accept_loop函数,在accept_loop中调用 gen_tcp:accpet/1接收新的tcp连接,从而达到不阻塞gen_server主循环的目的!看看accept_loop,我们收到一个连接时,判断是否达到最大连接数,如果达到我们则发送给client一个消息,随后关闭这个sock;正常情况下,我们首先同志gen_server一个新的连接加入,这样gen_server就通过accept创建一个新的process,来处理gen_tcp:accept/1函数,接收用户请求。而当前的process来处理具体的业务逻辑,比如一个echo server。
为什么这么做呢?因为通过调用gen_tcp:accept/1创建的Socket所在的process成为此Socket的controlling process,如果其关闭我们的socket也会关闭!所以我们便把这个process留给这个连接,让他处理具体的逻辑:M:F(Sock),注意这里相当于一个会调的函数,具体的我们要实现什么server,就需要实现什么逻辑。
还有一个情况,我们要限定并发总数,因此在gen_tcp:accept/1中,我们通过判断是否达到最大连接数而决定是否关闭这个新接入的socket,这样就可以限定连接总数。
好的,让我们看看,我实现的一个echo_server:
十几行代码就解决了问题!首先调用generic_server:start启动我们的tcp server,我们将其命名为echo_server,我们还指定了绑定端口,最大连接数,逻辑处理回调函数。
以后只要有用户连接成功server,就会进入我们的loop循环,我们在这里可以做的逻辑,我们这里只是接收用户的数据,然后将数据原封不动的返回给client,这就实现了一个echo server。需要说明的是,我们的tcp server在创建的时候指定了{active, false}选项,需要手动调用gen_tcp:recv/2接收数据,如果收到{error, closed},表明socket已经被client关闭,我们调用gen_server:cast通知并发数减一。
最后写一个测试的tcp client,其连接我们的echo server,发送数据。
下面编译所有的模块,实验一下吧:
启动两个erl控制台:A server;B client
在A erl控制台启动server:
B erl控制台运行:
A 控制台显示:
继续...
B:
A:
关闭某个socket:
B:
A:
创建socket -> 绑定端口 -> listen监听 -> accept tcp 连接 -> 处理业务 -> 关闭连接。中间可能会有多线程或者线程池等等不同的实现方式。
在erlang的世界,我们还是需要绑定端口,接受连接,处理业务,关闭连接,但是我们没有什么线程,锁的烦恼。我们为每个连接建立一个process,处理业务。因为erlang的process轻量,高效,成千上万。
我们用gen_sever behaviour来实现这个通用的tcp server,gen_server其实内部包含一个大循环,如果我们的tcp server再gen_server的循环中调用gen_tcp:accept/1,那么我们会阻塞gen_server,这肯定行不通。那么只有将 gen_tcp:accept放到一个独立的process中了。
我们的generic_server中定义了一个record:
-record(server_state, { port, % 监听的端口 loop, % 具体的逻辑处理循环 ip=any, % 绑定的ip lsocket=null, % 监听socket conn=0, % 当前的连接数 maxconn % 最大的连接数 }).
这个record用来记录服务器的相关信息,注释已经相当清楚。
接下来让我们实现gen_server的init/1函数(如果对gen_server不熟悉,请参看以前文章,或者erlang官方文档),这个部分非常关键:
%% gen_server初始化,创建监听socket init(State = #server_state{port=Port}) -> case gen_tcp:listen(Port, ?TCP_OPTIONS) of {ok, LSocket} -> {ok, accept(State#server_state{lsocket=LSocket})}; {error, Reason} -> {stop, {create_listen_socket, Reason}} end.
创建listen 端口成功时,我们调用accept/1函数:
%% 生成一个新的process,用来accept tcp接入,同时返回初始化State accept(State =#server_state{lsocket=LSocket, loop=Loop, conn=Conn, maxconn=Max}) -> proc_lib:spawn(generic_server, accept_loop, [self(), LSocket, Loop, Conn, Max]), State.
accept_loop照此实现:
%% 接收新的tcp连接 accept_loop(Server, LSocket, {M, F}, Conn, Max) -> {ok, Sock} = gen_tcp:accept(LSocket), if Conn + 1 > Max -> io:format("reach the max connection~n"), gen_tcp:close(Sock); true -> gen_server:cast(Server, {accept_new, self()}), M:F(Sock) end.
看明白了么?
我们就是在accept里生成一个新的process,其执行accept_loop函数,在accept_loop中调用 gen_tcp:accpet/1接收新的tcp连接,从而达到不阻塞gen_server主循环的目的!看看accept_loop,我们收到一个连接时,判断是否达到最大连接数,如果达到我们则发送给client一个消息,随后关闭这个sock;正常情况下,我们首先同志gen_server一个新的连接加入,这样gen_server就通过accept创建一个新的process,来处理gen_tcp:accept/1函数,接收用户请求。而当前的process来处理具体的业务逻辑,比如一个echo server。
为什么这么做呢?因为通过调用gen_tcp:accept/1创建的Socket所在的process成为此Socket的controlling process,如果其关闭我们的socket也会关闭!所以我们便把这个process留给这个连接,让他处理具体的逻辑:M:F(Sock),注意这里相当于一个会调的函数,具体的我们要实现什么server,就需要实现什么逻辑。
还有一个情况,我们要限定并发总数,因此在gen_tcp:accept/1中,我们通过判断是否达到最大连接数而决定是否关闭这个新接入的socket,这样就可以限定连接总数。
好的,让我们看看,我实现的一个echo_server:
-module(echo_server). -export([start/2, loop/1]). %% @spec start(Port::integer(), Max::integer()) -> ServerRet %% @doc 启动echo server start(Port, Max) -> generic_server:start(echo_server, Port, Max, {?MODULE, loop}). %% @spec loop(Sock::port()) %% @doc 处理echo_server中用户的请求 loop(Sock) -> case gen_tcp:recv(Sock, 0) of {ok, Data} -> gen_tcp:send(Sock, Data), loop(Sock); {error, closed} -> io:format("client sock close~n"), gen_server:cast(echo_server, {connect_close, self()}) end.
十几行代码就解决了问题!首先调用generic_server:start启动我们的tcp server,我们将其命名为echo_server,我们还指定了绑定端口,最大连接数,逻辑处理回调函数。
以后只要有用户连接成功server,就会进入我们的loop循环,我们在这里可以做的逻辑,我们这里只是接收用户的数据,然后将数据原封不动的返回给client,这就实现了一个echo server。需要说明的是,我们的tcp server在创建的时候指定了{active, false}选项,需要手动调用gen_tcp:recv/2接收数据,如果收到{error, closed},表明socket已经被client关闭,我们调用gen_server:cast通知并发数减一。
最后写一个测试的tcp client,其连接我们的echo server,发送数据。
-module(tcp_client). -export([start/1, send_data/2, close/1]). start(Port) -> {ok, Socket} = gen_tcp:connect("127.0.0.1", Port, [binary, {packet, raw}, {active, true}, {reuseaddr, true}]), Socket. send_data(Socket, Data) when is_list(Data) orelse is_binary(Data) -> gen_tcp:send(Socket, Data), receive {tcp, Socket, Bin} -> io:format("recv ~p~n", [Bin]); {tcp_closed, Socket} -> io:format("remote server closed!~n") end. close(Socket) when is_port(Socket) -> gen_tcp:close(Socket).
下面编译所有的模块,实验一下吧:
> c(generic_server). {ok, generic_server} > c(echo_server). {ok, echo_server} > c(tcp_client). {ok, tcp_client}
启动两个erl控制台:A server;B client
在A erl控制台启动server:
1> echo_server:start(1234, 4). max connection is 4 {ok,<0.31.0>}
B erl控制台运行:
1>Sock = tcp_client:start(1234). #Port<0.140>
A 控制台显示:
current connect:1
继续...
B:
2>Sock2 = tcp_client:start(1234). #Port<0.141> 3> Sock3 = tcp_client:start(1234). #Port<0.142> 4> Sock4 = tcp_client:start(1234). #Port<0.143> 4> Sock5 = tcp_client:start(1234). #Port<0.144>
A:
current connect:2 current connect:3 current connect:4 reach the max connection
关闭某个socket:
B:
> tcp_client:close(Sock4). ok
A:
> client sock close current connect:3
- generic_server.zip (1.9 KB)
- 下载次数: 255
评论
4 楼
宋兵甲
2014-03-25
在跑这个服务的时候,每秒建立一个客户端连接,连续建立100000个,发现有10000多个Socket连接建立超时。不知道是什么原因。
3 楼
falood
2013-03-25
仔细看了一下,貌似有点 bug,accept_loop 函数在链接数超出限制后,就会停止工作了,不再接收新的请求,即使有链接被释放。
在 gen_tcp:close(Sock); 之后重新启动 accept_loop 可以解决这个问题,但 Conn 和 Max 无法与 gen_server 的 status 同步,还在纠结中,期待楼主的解决方案。
在 gen_tcp:close(Sock); 之后重新启动 accept_loop 可以解决这个问题,但 Conn 和 Max 无法与 gen_server 的 status 同步,还在纠结中,期待楼主的解决方案。
2 楼
wrj913
2012-01-06
M:F(Sock),什么时候执行
1 楼
suchuan19890730
2011-10-26
写的真的很好。对我帮助很大。我现在在想,写的gen_sever的服务器,当接收到connection的时候,spawn一个 FSM process 来处理这个connection连接 会不会更好,因为毕竟在tcp传输的时候,并不是已建立连接就会trasmite data 还要经过handshake等等 才会最终开始传输数据的,您说不是吗? 不知道您对此有何看法,期待您的回复。
发表评论
-
Erlang问答网站,欢迎各位提出问题,解答问题。
2012-03-18 15:07 5323平时收到很多关于Erlang的问题,我都尽量一一解答,可是时间 ... -
Emakefile并行编译
2011-11-17 13:15 7682项目代码越来越多,使用erlang编译也越来越慢。无论是Mak ... -
Erlang服务器内存耗尽bug跟踪过程
2011-10-25 21:44 21917本文描述朋友Erlang服务器内存耗尽bug的解决过程 ... -
inet:getstat/2小用法
2011-04-27 09:32 4603inet:getstat/2的用处 在 ... -
Erlang游戏开发-协议
2011-04-22 16:10 10745Erlang游戏开发-协议 ... -
Gearman Erlang Client
2010-10-17 21:14 3738Gearman Gearman是一个通用的任务调度框架。 ... -
ECUG归来
2010-10-17 21:02 2996今天ECUG V圆满结束了,不知不觉作为讲师已经参加过3次大会 ... -
gen-erl-app快速生成erlang app 框架
2010-04-07 14:22 4017经常需要创建各种erlang app,这个过程一旦掌握,就很繁 ... -
erl-redis发布
2010-03-30 11:44 5809最近几天因为需要,实现了一个redis erlang clie ... -
用Erlang做了很多事
2010-01-19 14:08 5103因为工作及时间关系,最近比较忙碌,没有太多的时间写文章。 ... -
ecug topic - erlang开发实践
2009-11-11 10:04 3777从ecug归来,感觉不错,大家学习探讨的积极性很高哦。 很高 ... -
reltool用户指南
2009-11-02 22:27 6412说明,最近比较忙,没有太多时间更新blog,请各位朋友谅解. ... -
Erlang定时任务server (仿crontab语法)
2009-09-23 18:03 6400好久不写blog了,看到yufeng老大那么活跃,我也“耐不住 ... -
Erlang进程之错?
2009-07-27 15:06 3709前阵子erlang-china关于erla ... -
CNode指南
2009-07-27 14:13 3365好久不发文章,因为工作太忙。这个东西就凑凑数吧。各位见谅。 ... -
Erlang类型及函数声明规格
2009-06-08 22:41 9592Erlang类型及函数声明 ... -
使用etop查看系统中进程信息
2009-05-29 13:57 6200Erlang提供了丰富的开发工具,你认为没有的时候,很可能是你 ... -
创建gen_server组解决单process瓶颈
2009-05-27 17:05 5299并发和顺序是一个令人 ... -
又有人投入Erlang的怀抱了:37Signals Campfire loves Erlang
2009-05-14 23:00 3698就喜欢看这样的东西... This is so juicy ... -
list random shuffle实现
2009-05-07 13:41 4370在项目中需要对list进行随机shuffle,但是在erlan ...
相关推荐
4. 同步UDP服务器:使用Socket的ReceiveFrom方法接收数据报,服务器会阻塞直到接收到一个数据报,然后处理并返回响应。 5. TcpListener类:是.NET Framework提供的用于TCP服务器的抽象类,它可以监听指定端口的TCP...
在IT行业中,网络通信是至关重要的一个领域,TCP(Transmission Control Protocol)作为互联网协议栈中的主力,被广泛用于实现客户端与服务器之间的可靠数据传输。在这个经典的TCP客户端服务器实例中,我们将探讨TCP...
2. **建立TCP连接**:使用如lwIP或FreeRTOS+TCP这样的实时操作系统网络栈,创建一个TCP服务器,监听特定端口。当有客户端连接请求时,接受连接并建立套接字。 3. **串口与TCP的桥接**:编写程序逻辑,使STM32F4能够...
MODBUS-TCP是MODBUS协议的一个变种,将MODBUS的命令结构与TCP/IP结合,使其能够适应以太网环境。MODBUS-TCP允许设备通过TCP连接进行数据交换,提高了通信速度和可靠性,广泛应用于分布式控制系统和智能设备间的数据...
本项目“TCP实现客户机/服务器聊天”利用C++编程语言和Microsoft Foundation Classes (MFC)库来构建一个简单的聊天应用,它演示了如何使用TCP进行客户端与服务器端的交互。 TCP是一种面向连接的、可靠的传输协议,...
总之,通过Kepserver的Modbus RTU从站和Modbus TCP服务器配置,我们可以构建一个桥梁,连接不同协议的设备,实现自动化系统的无缝集成。这不仅提高了数据传输的效率,也降低了系统集成的复杂性,对于提升工业自动化...
MODBUS-TCP是MODBUS协议的一个变种,它在物理层和数据链路层使用以太网标准,而在传输层则利用TCP提供连接。MODBUS协议最初设计用于串行通信,后来为了适应网络环境,发展出了MODBUS-TCP。MODBUS-TCP协议保留了...
总结来说,“基于线程的TCP聊天服务器”项目展示了如何利用C++和多线程技术来构建一个能处理多个并发连接的服务器。这种设计允许服务器高效地为多个用户提供服务,是网络编程中的一个经典案例。
Modbus TCP是Modbus协议的一个扩展,它将传统的串行通信方式升级到网络化的TCP/IP协议栈,使得远程控制和数据采集系统(SCADA)能够通过以太网实现高效的数据交换。 首先,我们需要了解Modbus协议的基本概念。...
修改一次最多读写寄存器个数120个。 4.增加WIN7等高版本系统支持。 5.欢迎交流,指正,本软件免费。 软件运行要求: 1.WINCE 系统上可以直接运行。 2.WINXP 系统上没装 VS2005 或更高版的 VS 软件的用户,可以到...
在本项目中,"linux写的基于epoll技术的socket tcp服务器,数据库采用mysql.zip",开发者利用了Linux的epoll机制来实现了一个高效的TCP服务器,并结合MySQL作为后台数据库存储数据。以下是关于这些关键知识点的详细...
"Modbus TCP的OPC服务器设计" 本文主要介绍了基于Modbus TCP的OPC服务器设计相关知识点,旨在解决工业自动化领域中设备之间的通信异常复杂的问题。 OPC技术 OPC(OLE for Process Control,用于过程控制的对象...
综上所述,这个“TCP聊天服务器客户端”项目是一个综合性的学习平台,涵盖了网络编程、多线程、并发控制、接口设计、事件驱动、C/S架构、通信协议、UI交互、数据序列化和压缩等多个核心IT技术,对于提升C#开发者在...
MODBUS_TCP协议的客户机和服务器应用指导包括客户机设计、服务器设计、多线程服务器、单线程服务器、必需的及期望的性能等。 【知识点7:MODBUS_TCP协议的非指令数据的编码】 MODBUS_TCP协议的非指令数据的编码...
首先,要创建一个FTP服务器,你需要理解TCP/IP协议栈,因为FTP基于TCP协议。在Java中,`java.net.ServerSocket`类用于创建监听特定端口的服务器端套接字,等待客户端的连接。一旦有客户端连接,服务器会创建一个新的...
当一个TCP客户端发起连接请求时,服务器会接受连接,并在两者之间创建一个TCP连接。在这个连接上,服务器可以接收客户端发送的数据,并向客户端发送响应。TCP服务器广泛应用于各种网络服务,如Web服务器(HTTP/HTTPS...
一个串口转TCP的程序,能很好的满足远程串口传输、调试需求,基本特征如下: 1、支持打开物理串口和虚拟串口(不创建虚拟串口,但能打开其他工具创建的虚拟串口)。 2、支持通过TCP客户端连接到远程TCP服务器。 3、...
SocketTcp_TcpSocket服务端框架是一个专为开发者设计的简单且通用性强的TCP服务器实现。这个框架旨在简化网络编程过程,让开发者能够快速构建自己的TCP服务应用,只需要对原有代码进行少量修改,即可轻松地将其集成...
AT指令集是ESP8266的核心控制手段,它是一种通用的命令集,用于配置模块的网络参数、建立和断开TCP连接、发送和接收数据等。例如,`AT+CIPSTART`命令用于启动TCP连接,`AT+CIPSEND`用来发送数据,而`AT+CIPCLOSE`则...