`
gashero
  • 浏览: 943970 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Erlang的C Node

阅读更多

 

Erlang的C Node

翻译: 日期: 地址:
gashero
2009-10-16
http://www.erlang.org/doc/tutorial/cnode.html

这个例子是用于解决 http://www.erlang.org/doc/tutorial/example.html 问题的。注意C node不适用于解决简单问题,port会更合适。

1   Erlang程序

从Erlang的观点来看,C node只是一个普通的Erlang结点。因此调用函数foo和bar只是发送消息到C node请求调用函数,和接受结果。发送消息需要接受者;进程可以定义为pid或包含注册进程名和结点名的元组。这种情况下元组只是唯一选择,因为根本就 没有pid。

{RegName,Node} ! Msg

结点名Node是C node的名字。如果使用短结点名,则名字形如 cN 其中N是整数。如果使用长结点名,就没有这么严格了。一个使用短结点名的例子是 c1@idril ,一个使用长结点名的例子是 cnode@idril.ericsson.se

注册名 RegName 可以是任意原子。这个名字会被C代码忽略,或者用于辨别不同类型的消息。如下是Erlang代码使用短结点名的例子:

-module(complex3).
-export([foo/1,bar/1]).
foo(X) ->
    call_cnode({foo,X}).
bar(Y) ->
    call_cnode({bar,Y}).
call_cnode(Msg) ->
    {any,c1@idril} ! {call,self(),Msg},
    receive
        {cnode,Result} ->
            Result
    end.

当使用长结点名时,也只有略微不同,如下只列出 call_cnode() 函数:

call_cnode(Msg) ->
    {any,'cnode@idril.du.uab.ericsson.se'} ! {call,self(),Msg},
    receive
        {cnode,Result}
            Result
    end.

2   C程序

2.1   设置通信

在开始调用 Erl_interface 函数之前,内存处理需要初始化:

erl_init(NULL,0);

现在C结点已经初始化了。如果使用了短结点名,则使用 erl_connect_init()

erl_connect_init(1,"secretcookie",0);

第一个参数是整数用于构造结点名。这个例子中纯结点名是"c1"。第二个参数是字符串描述的magic cookie,用于识别结点所在的群组。第三个参数是整数,用于标识一个C node的实例。

如果使用长结点名,初始化是如下调用:

erl_connect_xinit("idril","cnode","cnode@idril.ericsson.se",&addr,"secretcookie",0);

前面的3个参数是主机名、纯结点名和完整结点名。第四个参数是指向 in_addr 结构体的指针,标识主机的IP地址,第五个和第六个参数是magic cookie和实例号。

C node可以作为服务器也可以作为客户端与Erlang-C通信。如果作为客户端,它使用 erl_connect() 连接到一个Erlang结点,然后成功返回打开的文件描述符:

fd=erl_connect("e1@idril");

如果C node作为服务器,他必须首先创建socket(调用bind()和listen())和监听合适的端口。然后发布自己的名字和端口号到 epmd (erlang端口映射守护进程,查看epmd的手册):

erl_publish(port);

现在C node服务器可以接受Erlang结点的连接了:

fd=erl_accept(listen,&conn);

第二个参数是 ErlConnect 结构体,包含了连接的有用信息;例如Erlang结点名。

2.2   发送和接受消息

C node可以用 erl_receive_msg() 接收消息。这个函数从打开的文件描述符fd读取数据到缓冲区,然后将结果存入 ErlMessage 类型结构体 emsg 。 ErlMessage 结构体拥有一个字段 type 定义了接受到的消息类型。这种情况下 ERL_REG_SEND 标识接受到了注册进程的消息。手机的消息,一个 ETERM 存放在 msg 字段中。

需要注意的是 ERL_ERROR 类型和 ERL_TICK 类型,分别对应错误和存活检查。其他类型大概是link/unlink和exit之类的。

while(loop) {
    got=erl_receive_msg(fd,buf,BUFSIZE,&emsg);
    if (got=ERL_TICK) { //忽略即可
    } else if(got==ERL_ERROR) {
        loop=0;
    } else {
        if (emsg.type==ERL_REG_SEND) {

消息是一个 ETERM 结构体, erl_interface 函数可以用于操作它。这种情况下,消息是一个三元组(与Erlang代码有关,看看上面)。第二个元素是调用者的pid,第三个元素是元组 {Function,Arg} 决定使用何种参数调用哪个函数。函数调用的结果放入 ETERM 结构体,然后用 erl_send() 发送回去,附带上打开的文件描述符、pid和术语作为参数。

fromp=erl_element(2,emsg.msg);
tuplep=erl_element(3,emsg.msg);
fnp=erl_element(1,tuplep);
argp=erl_element(2,tuplep);

if (strncmp(ERL_ATOM_PTR(fnp),"foo",3)==0) {
    res=foo(ERL_INT_VALUE(argp));
}else if (strncmp(ERL_ATOM_PTR(fnp),"bar",3)==0) {
    res=bar(ERL_INT_VALUE(argp));
}
resp=erl_format("{cnode,~i}",res);
erl_send(fd,fromp,resp);

最后 ETERM 结构体创建函数(包括 erl_receive_msg() 分配的内存)必须释放掉:

erl_free_term(emsg.from);
erl_free_term(emsg.msg);
erl_free_term(fromp);
erl_free_term(tuplep);
erl_free_term(fnp);
erl_free_term(argp);
erl_free_term(resp);

最终的C程序有如下面的样子。第一个C node服务器使用短结点名:

/* cnode_s.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "erl_interface.h"
#include "ei.h"

#define BUFSIZE 1000

int main(int argc, char ** argv) {
    int port;               //监听端口号
    int listen;             //监听socket
    int fd;                 //Erlang结点的文件句柄
    ErlConnect conn;        //连接数据

    int loop=1;
    int got;
    unsigned char buf[BUFSIZE];
    ErlMessage emsg;
    ETERM *fromp, *tuplep, *fnp, *argp, *resp;
    int res;

    port=atoi(argv[1]);

    erl_init(NULL,0);       //初始化c node
    if (erl_connect_init(1,"secretcookie",0)==-1)
        erl_err_quit("erl_connect_init");
    if ((listen=my_listen(port))<=0)
        erl_err_quit("my_listen");
    if (erl_publish(port)==-1)
        erl_err_quit("erl_publish");
    if ((fd=erl_accept(listen,&conn)==ERL_ERROR)
        erl_err_quit("erl_accept");
    fprintf(stderr,"Connected to %s\n\r",conn.nodename);

    while(loop) {           //循环接受消息请求
        got=erl_receive_msg(fd,buf,BUFSIZE,&emsg);
        if (got==ERL_TICK) {
            //忽略
        }else if (got==ERL_ERROR) {
            loop=0;
        }else {
            if (emsg.type==ERL_REG_SEND) {
                fropm=erl_element(2,emsg.msg);
                tuplep=erl_element(3,emsg.msg);
                fnp=erl_element(1,tuplep);
                argp=erl_element(2,tuplep);
                if (strncmp(ERL_ATOM_PTR(fnp),"foo",3)==0) {
                    res=foo(ERL_INT_VALUE(argp));
                }else if (strncmp(ERL_ATOM_PTR(fnp),"bar",3)==0) {
                    res=bar(ERL_INT_VALUE(argp));
                }
                resp=erl_format("{cnode,~i}",res);
                erl_send(fd,fromp,resp);
                erl_free_term(emsg.from);
                erl_free_term(emsg.msg);
                erl_free_term(fromp);
                erl_free_term(tuplep);
                erl_free_term(fnp);
                erl_free_term(argp);
                erl_free_term(resp);
            }
        }
    }
}

int my_listen(int port) {
    int listen_fd;
    struct sockaddr_in addr;
    int on=1;
    if ((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
        return (-1);
    setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    memset((void*)&addr,0,(size_t)sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    if (bind(listen_fd,(struct sockaddr*)&addr,sizeof(addr))<0)
        return(-1);
    listen(listen_fd,5);
    return listen_fd;
}

下面的例子是长结点名模式的,区别只是在于 erl_connect_xinit()

下面的例子是C node客户端:

/* cnode_c.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "erl_interface.h"
#include "ei.h"

#define BUFSIZE 1000

int main(int argc, char ** argv) {
    int fd;
    int loop=1;
    int got;
    unsigned char buf[BUFSIZE];
    ErlMessage emsg;
    ETERM *fromp, *tuplep, *fnp, *argp, *resp;
    int res;

    erl_init(NULL,0);
    if (erl_connect_init(1,"secretcookie",0)==-1)
        erl_err_quit("erl_connect_init");
    if ((fd=erl_connect("e1@idril"))<0)
        erl_err_quit("erl_connect");
    fprintf(stderr,"Connected to ei@idril\n\r");
    while(loop) {
        got=erl_receive_msg(fd,buf,BUFSIZE,&emsg);
        if (got=ERL_TICK) {
            //忽略
        }else if (got==ERL_ERROR) {
            loop=0;
        }else {
            if (emsg.type==ERL_REG_SEND) {
                fromp=erl_element(2,emsg.msg);
                tuplep=erl_element(3,emsg.msg);
                fnp=erl_element(1,tuplep);
                argp=erl_element(2,tuplep);
                if (strncmp(ERL_ATOM_PTR(fnp),"foo",3)==0) {
                    res=foo(ERL_INT_VALUE(argp));
                }else if (strncmp(ERL_ATOM_PTR(fnp),"bar",3)==0) {
                    res=bar(ERL_INT_VALUE(argp));
                }
                resp=erl_format("{cnode,~i}",res);
                erl_send(fd,fromp,resp);
                erl_free_term(emsg.from);
                erl_free_term(emsg.msg);
                erl_free_term(fromp);
                erl_free_term(tuplep);
                erl_free_term(fnp);
                erl_free_term(argp);
                erl_free_term(resp);
            }
        }
    }
}

3   运行例子

3.1   编译C代码

编译C代码,提供Erl_interface的头文件和库文件路径,还有socket和nsl库。

在R5B或更新版本的OTP中,头文件和库路径在 OTPROOT/lib/erl_interface-VSN ,其中OTPROOT是你的OTP安装路径,VSN是 erl_interface 的应用版本(上面例子是3.2.1。

在R4B或更早版本的OTP中,头文件和库路径在 OTPROOT/usr

编译选项:

  1. -I/usr/local/lib/otp/lib/erl_interface-3.2.1/include
  2. -L/usr/local/lib/otp/lib/erl_interface-3.2.1/lib
  3. -lerl_interface
  4. -lei
  5. -lsocket
  6. -lnsl

3.2   编译erlang代码

unix> erl -compile complex3 complex4

3.3   运行C node服务器的例子,短结点名

启动C程序 cserver 和Erlang。cserver以端口号作为参数,且在调用Erlang函数前启动。Erlang结点需要给出短结点名 "e1" 并设置与C node相同的magic cookie。

unix> cserver 3456
unix> erl -sname e1 -setcookie secretcookie
Erlang (BEAM) emulator version 4.9.1.2
Eshell V4.9.1.2  (abort with ^G)
(e1@idril)1> complex3:foo(3).
4
(e1@idril)2> complex3:bar(5).
10

3.4   运行C node客户端例子

停止cserver但是不停止Erlang,启动cclient。Erlang结点必须在C node客户端之前启动:

unix> cclient
(e1@idril)3> complex3:foo(3).
4
(e1@idril)4> complex3:bar(5).
10

3.5   运行C node服务器,长结点名的例子

unix> cserver2 3456
unix> erl -name e1 -setcookie secretcookie
Erlang (BEAM) emulator version 4.9.1.2
Eshell V4.9.1.2  (abort with ^G)
(e1@idril.du.uab.ericsson.se)1> complex4:foo(3).
4
(e1@idril.du.uab.ericsson.se)2> complex4:bar(5).
10
0
0
分享到:
评论

相关推荐

    python-erlang-interface:Python的Erlang接口

    派尔 PyErl是Python的Erlang接口。...pyerl.mk_string("world")int = pyerl.mk_int(-1)list = pyerl.mk_list([atom, string, int])print list呼叫rpc # just call pingpong:ping() in node2@localh

    oc_bifrost:已弃用

    Copyright 2014 Chef Software Inc Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License ...

    zmq中文指南(离线网页版).zip

    官方zeromq the guide... We've also translated most of the examples into C++, C#, CL, Delphi, Erlang, F#, Felix, Haskell, Objective-C, Ruby, Ada, Basic, Clojure, Go, Haxe, Node.js, ooc, Perl, and Scala.

    许可证依赖搜索LicenseFinder.zip

    LicenseFinder 能够为你的项目找到许可证依赖项。LicenseFinder 与你的软件包管理器一起使用能够找到其依赖项,能检测到包中的许可证,且许可证还能与用户定义的白名单比较,另外你能够... Objective-C ( CocoaPods)

    Elixir(函数式编程语言软件)v1.5官方免费正式版

    Elixir是一款免费的函数式编程语言,采用一种动态语言,灵活的语法和宏支持,建立在Erlang虚拟机之上,来构建并发、分布式、容错应用程序及热代码升级。软件功能强大,界面简洁,是您进行函数式编程的首选,需要的...

    RabbitMq消息队列指南.docx

    RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中...Ruby 、.NET 、PHP 、C/C++ 、node.js 等。

    C# Thrift测试

    它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务 ...

    Nami:适用于Node.js的Asterisk管理器界面(AMI)客户端

    您也可以从CI服务器下载发行版和文档,为: : : 在上可以找到非常相似PHP替代品,在上可以找到Erlang端口。 Nami本身只是一个库,它允许您的nodejs代码与Asterisk Manager Interface(AMI)进行通信。 但是,它...

    redis java client-jedis

    Redis支持很多编程语言的客户端,有C、C#、C++、Clojure、Common Lisp、Erlang、Go、Lua、Objective-C、PHP、Ruby、Scala,甚至更时髦的Node.js,当然,更少不了Java的客户端支持。Redis官方列出的Java客户端也有少...

    Computer Simulation Techniques: The definitive introduction!

    3.3.3 Sampling from an Erlang distribution, 51 3.3.4 Sampling from a normal distribution, 53 3.4 Sampling from a discrete-time probability distribution, 55 3.4.1 Sampling from a geometric distribution...

    rumprun:适用于各种平台的Rumprun unikernel和工具链

    Rumprun unikernel支持使用以下应用程序编写的应用程序,例如但不限于: C , C ++ , Erlang , Go , Java , Javascript(node.js) , Python , Ruby和Rust 。 您可以从找到Rumprun的。 那里提供的一些软件...

    Dash for Mac 2.0.2 文档查看神器 破解版

    iOS OS X .NET Framework Man Pages ActionScript Akka Android AngularJS Ansible Appcelerator Titanium Arduino Backbone Bash Boost Bootstrap Bourbon Bourbon Neat C C++ CakePHP Cappuccino Chai Chef ...

    Thrift功能简介

    例如:C ++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C# Cocoa,JavaScript,Node.js,Smalltalk,OCaml和Delphi等语言。 Thrift功能简介 在使用Thrift时,我们首先需要编写一个thrift文件。 这个文件是...

    消息队列RabbitMQ入门与5种模式详解

    简介:MQ全称为MessageQueue,消息队列是应用程序和应用程序之间的通信方法;RabbitMQ是开源的,实现了AMQP协议的,...支持多种客户端开发语言:Java、Python、Ruby、.NET,PHP、C/C++、Node.js等术语说明:Server(Brok

    Matlab代码verilog-timo-cmd:我的GitHub个人资料-readme:star::sparkles:

    编译器构造:Node.js和Golang 常识:Lua,Ruby,Crystal,Golang,Haskell,Elixir / Erlang,Dart / Flutter和Verilog 前缀板 :telescope: 我目前正在研究Haiku分散式区块链 :seedling: 我目前正在学习同构编译器的...

    java版飞机大战源码-ProgramBookTutorial2:ProgramBookTutorial2

    C/C++ (by @andycai) Clojure Dart Elixir Erlang Fortran Go Groovy Haskell iOS Java JavaScript () (PDF) jQuery Node.js underscore.js backbone.js (PDF) AngularJS Zepto.js Sea.js React.js impress.js ...

    CentOs 7.3中搭建RabbitMQ 3.6单机多实例服务的步骤与使用

    RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展...

    amqp:基于EventMachine的RabbitMQ客户端。 选择兔子:http://rubybunny.info。 请参阅http://rubyamqp.info上的文档指南

    Ruby Amqp gem:异步Ruby RabbitMQ客户端 是一个功能丰富的基于EventMachine的RabbitMQ客户端,... RabbitMQ是一种开源消息中间件,它强调不同技术(例如Java,.NET,Ruby,Python,Node.js,Erlang,Go,C等)之间的

    Y分钟学习X种语言

    Lua是一种轻量级的动态编程语言,对协程(coroutine)有着很好的支持,因为有着简单的C API,使得它在游戏视频脚本处理方面非常流行。比如游戏引擎LÖVE 和Marmalade Quick。 尝试Clojure语言 Clojure是大演讲家Rich ...

    fib:Github顶级语言的性能基准

    其他:Crystal,Rust,Swift,Mono,Elixir,Perl,R,Julia,D,Nim,Pascal,Fortran,Cython,Pony,OCaml,Lisp,Haskell,Erlang,Escript,Dart,Clojure,Scheme,Lua,Python3, Perl,Perl6,Bash,Emoji ...

Global site tag (gtag.js) - Google Analytics