erlang 与外部通信有2种方式:
A. 外部port,外部程序通过stdin,stdout与erlang交互
B. linkin driver 是erlang 直接调用的方式
从效率上看,当然是B的效率最好,一直觉得这个东西是非常有用的,以前也有些idea,请教过yufeng老大,结果被教育了,所以一直不敢乱用,打算从gen_tcp开始学习driver的编写
从源码上看,gen_tcp 模块在erlang这边只是很薄的一层API,最终都是通过prim_inet.erl调用的inet_drv.c (我只关心了IPv4的实现)
erlang调用 driver大体也有2种方法
A. port_command 或者是 port ! message的方式
这种方式的调用,返回值需要通过receive方法得到
B. port_control
这种方式的调用,可以直接得到返回值,manual上讲这种方式也是最快的
gen_tcp里大部分API是通过B方式调用的
%% Control command
ctl_cmd(Port, Cmd, Args) ->
?DBG_FORMAT("prim_inet:ctl_cmd(~p, ~p, ~p)~n", [Port,Cmd,Args]),
Result =
try erlang:port_control(Port, Cmd, Args) of
[?INET_REP_OK|Reply] -> {ok,Reply};
[?INET_REP_SCTP] -> {error,sctp_reply};
[?INET_REP_ERROR|Err] -> {error,list_to_atom(Err)}
catch
error:_ -> {error,einval}
end,
?DBG_FORMAT("prim_inet:ctl_cmd() -> ~p~n", [Result]),
Result.
这段方法就是prim_inet里封装port调用及返回值的代码,调用driver里对应的是
/* TCP requests from Erlang */
static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len,
char** rbuf, int rsize)
{
tcp_descriptor* desc = (tcp_descriptor*)e;
switch(cmd) {
case INET_REQ_OPEN:
。。。
}
}
能够看到driver通过erlang传过来的cmd 来区分不同的请求
这个方法的参数含义具体如下:
cmd 就是 erlang 调用port_control的第2个参数cmd
buf 是erlang 调用port_control的第3个参数args
len 是erlang传递args的长度
rbuf 是返回值buf
rsize 是这个用于返回的buf的初始大小
driver里对返回值处理也是通用的一个函数
/* general control reply function */
static int ctl_reply(int rep, char* buf, int len, char** rbuf, int rsize)
{
char* ptr;
if ((len+1) > rsize) {
ptr = ALLOC(len+1);
*rbuf = ptr;
}
else
ptr = *rbuf;
*ptr++ = rep;
memcpy(ptr, buf, len);
return len+1;
}
也就是说,返回格式就是 resp code(1字节) + resp msg(len字节)的格式
然后拷贝buf到返回值rbuf中,rsize会提示当前返回值rbuf的初始大小,driver根据需要来重新alloc这个rbuf
写个简单的例子,印证下
example_drv.c
#include <stdio.h>
#include <strings.h>
#include <stdarg.h>
#include <time.h>
#include <stdlib.h>
#include "erl_driver.h"
#include <ei.h>
typedef struct {
ErlDrvPort port;
} example_data;
static void logmsg(char *fmt, ...){
va_list args;
FILE *logfile=fopen("./driver.log","a+");
if(logfile!=NULL){
time_t now=time(NULL);
struct tm *now_tm=localtime(&now);
char date[80];
strftime(date,sizeof(date),"%D %H:%M:%S",now_tm);
fprintf(logfile,"[%s] ",date);
va_start(args,fmt);
vfprintf(logfile,fmt,args);
va_end(args);
fclose(logfile);
}
}
static ErlDrvData example_drv_start(ErlDrvPort port, char *buff)
{
example_data* d = (example_data*)driver_alloc(sizeof(example_data));
d->port = port;
return (ErlDrvData)d;
}
static void example_drv_stop(ErlDrvData handle)
{
driver_free((char*)handle);
}
static void example_drv_output(ErlDrvData handle, char *buff, int bufflen)
{
char *str;
str=(char *)malloc(sizeof(*str)*bufflen+1);
strcpy(str,buff);
ei_x_buff x;
ei_x_new_with_version(&x);
ei_x_encode_tuple_header(&x, 2);
ei_x_encode_atom(&x, "echoback");
ei_x_encode_string(&x, str);
example_data* d = (example_data*)handle;
logmsg("output successful\n");
driver_output(d->port, x.buff,x.index );
}
static int port_ctl(ErlDrvData handle, unsigned int cmd, char* buf, int len,
char** rbuf, int rsize){
logmsg("request cmd=%d,buf=%s,len=%d,rsize=%d\n",cmd,buf,len,rsize);
char *ptr=*rbuf;
char *s="port ctl back";
strcpy(ptr,s);
return strlen(s);
}
ErlDrvEntry example_driver_entry = {
NULL,
example_drv_start,
example_drv_stop,
example_drv_output,
NULL,
NULL,
"example_drv",
NULL,
NULL,
port_ctl,
NULL,
NULL
};
DRIVER_INIT(example_drv) /* must match name in driver_entry */
{
return &example_driver_entry;
}
logmsg用于打印在driver中的日志
test_port.erl
-module(test_port).
-export([start/1, init/1,test/1,ctl/2]).
start(SharedLib) ->
case erl_ddll:load_driver(".", SharedLib) of
ok -> ok;
{error, already_loaded} -> ok;
R ->
io:format("could not load driver ~p~n",[R]),
exit({error, could_not_load_driver})
end,
spawn(?MODULE, init, [SharedLib]).
init(SharedLib) ->
register(complex, self()),
Port = open_port({spawn, SharedLib}, [binary]),
loop(Port).
test(Msg) ->
complex ! {call, Msg}.
ctl(Cmd,Msg)->
complex ! {ctl,Cmd,Msg}.
loop(Port) ->
receive
{call, Msg} ->
Port ! {self(), {command, Msg}},
receive
{Port, {data, Data}} ->
io:format("recv port data ~p~n",[Data])
end,
loop(Port);
{ctl,Cmd,Msg}->
Resp=erlang:port_control(Port, Cmd, Msg),
io:format("recv ctl data ~p~n",[Resp]),
loop(Port);
{'EXIT', Port, Reason} ->
io:format("~p ~n", [Reason]),
exit(port_terminated)
end.
编译driver
gcc -I/usr/local/erlang/lib/erlang/lib/erl_interface-3.5.9/include/ -I/usr/local/erlang/lib/erlang/erts-5.6.5/include/ -o example_drv.so -fpic -shared -L/usr/local/erlang/lib/erlang/lib/erl_interface-3.5.9/lib example_drv.c -lei -lerl_interface
调用 test_port:start("example_drv").
test_port:ctl(1,"hi port control").
driver打印日志:
[10/06/09 14:43:50] request cmd=1,buf=hi port control,len=15,rsize=64
并且shell返回消息正常
今天就先学这些,计划每天都看一点,另外很感谢mryufeng老大总是有问必答,在我学习erlang过程中给予了非常大的帮助,呵呵
分享到:
相关推荐
erlang-gen_tcp手册,详细完整,网络tcp开发好东东
在Erlang编程环境中,`gen_tcp`是一个用于TCP连接的通用接口,它提供了丰富的功能,如打开、接受、发送、接收和关闭TCP连接。在实际应用中,开发者可能会遇到`gen_tcp`的`close`函数与`delay_send`选项之间的交互...
基于Erlang的gen_tcp库是其强大的网络编程接口,用于实现TCP协议的应用。在这个场景中,我们讨论的是一个使用Erlang和gen_tcp编写的聊天室应用。 **gen_tcp简介** gen_tcp是Erlang OTP(开放电信平台)提供的一种...
通用 TCP 服务器 通用 TCP 服务器( gen_tcp_server ) 是一种 Erlang 行为,提供快速简便的方法将 TCP 服务器功能添加到您的应用程序。 它被实现为管理 TCP 连接的主管,因为它是孩子。如何使用它? 运行make来构建。...
在IT行业中,`gen_server`是Erlang OTP(开放电信平台)框架中的一个核心组件,它提供了一种模式化的方式来实现服务器进程。本篇博客“gen_server tasting 之超简单名称服务(续)”主要探讨了如何使用gen_server来...
4. **gen_tcp模块**:这个模块提供了处理TCP连接的函数,如`gen_tcp:connect/3`用于建立客户端连接,`gen_tcp:recv/2,3`用于接收数据,`gen_tcp:send/2`用于发送数据,以及`gen_tcp:close/1`用于关闭连接。...
在本文中,我们将深入探讨如何使用Erlang构建一个简易的聊天室,主要涉及的关键技术是gen_tcp和gen_server。Erlang是一种并发性极强、面向进程的编程语言,特别适合于构建高可用性和可扩展性的分布式系统,如聊天室...
- 在Erlang中,我们可以使用`gen_tcp`模块来操作TCP连接,包括监听、连接、发送和接收数据,以及关闭连接。 2. **Erlang的并发模型**: - Erlang的进程模型使得每个TCP连接可以作为一个独立的进程运行,这样可以...
标题中的"otp_win64_20.3-Erlang.zip"表明这是一个包含Erlang编程语言特定版本的Windows 64位版本的压缩文件。OTP(Open Telecom Platform)是Erlang的主要实现,它是一个用于构建高度并发、容错且可扩展的系统平台...
在"erlang_otp_win64_25.0"这个标题中,我们可以提取出几个关键点: 1. **Erlang**:这是一种函数式编程语言,以其在处理并发性和容错性方面的强大能力而闻名。Erlang的设计理念是让程序员能够轻松地构建能够并行...
esl-erlang_23.0和rabbitmq-3.8.4windows版本 直接下载安装就行,可以直接下载就可安装,非常的方便 ,欢迎大家下载 注意事项: 1. Erlang版本和RabbitMQ版本要配套 (Erlang23.0, RabbitMQ3.8.4) 2. amd芯片请乖乖...
《Erlang gen_server在OcamlAsync中的实现探索》 Erlang的gen_server是其并发模型的核心组件,它提供了一种强大的状态管理和错误处理机制。而在OCaml语言中,尽管有着自己的并发库如Async,但直接移植或模仿Erlang...
Erlang B 和 Erlang C 是在电信领域中广泛使用的两个数学公式,用于预测和管理电话交换系统的呼叫处理能力。这两个公式由丹麦工程师 Agner Krarup Erlang 在20世纪初开发,对于理解通信系统中的呼叫占用率、阻塞率和...
gen_smtp Erlang SMTP客户端和服务器库。 使命 提供通用的Erlang SMTP服务器框架,可以通过OTP样式的回调模块进行扩展。 还包括一个纯Erlang SMTP客户端。 目的是使在Erlang中收发电子邮件变得容易,而又省却POP / ...
##gs_tcp 这是我们游戏服务器,启动监听通用过程,由于项目之间共享通过复制粘贴,很难维护,我独立成一个App,然后添加一行gs_tcp:listen就可以了。主要实现是通过传一个模块,然后里面实现start_reader,然后tcp_...
Erlang OTP设计原则中的Gen_Fsm行为是一个关键的概念,用于构建健壮、可扩展的并发应用程序。Gen_Fsm,即通用有限状态机,是一种行为模式,它提供了一种结构化的方法来处理具有多种状态和事件的系统。本文将深入探讨...
在"erlang_standard_snippets-源码.rar"这个压缩包中,我们可以期待找到一些Erlang编程的代码片段,这些片段可能包含了Erlang语言的标准实践和常见模式。 Erlang的设计理念是基于actor模型,每个进程(process)都...
首先,我们来看看`gen_server`。它是一个行为(behaviour),定义了一组回调函数,这些函数构成了服务器的基本操作,如初始化、处理呼叫、处理Cast消息、处理定时事件等。通过实现这些回调函数,我们可以创建具有...
《Erlang程序设计》是Joe Armstrong所著的一本经典书籍,主要面向对并发编程和分布式系统感兴趣的读者。这本书的第二版提供了更深入的Erlang语言教程,以及丰富的实践案例,帮助读者理解和掌握Erlang的核心特性。...
用于测量Erlang中TCP数据接收性能的源代码组。 各种无组织和技术性的。 建造 $ git clone git://github.com/sile/recvbench.git $ cd recvbench $ ./rebar get-deps compile 使用例 $ erl - pz ebin deps /*/ ebin ...