Erlang与C构建的节点通讯

Erlang与C构建的节点通讯

Erlang节点之间的通讯,主要用于两个Erlang节点之间的通讯,但Erlang还支持与java构建的节点通讯,甚至与c构建的节点通讯,前面两种方式在我以前的文章都有讲到,所以这里讲Erlang与c构建的节点通讯。

想用C构建一个erlang节点,要利用Erlang的erl_interface接口来实现。c建立的节点,叫CNode ,其中,erl_interface除了实现一些基本的节点连接,消息发送接收,还实现Erlang Term 的构建解析。

CNode是除了nif之外的另一种c扩展erlang方式,但对比nif来说这种方式不会引起erlang的crash,因为他们在不同的进程上,底层通过socket通讯。

另外一个问题,CNode在erlang集群中节点是隐藏的,所以在erlang中用nodes()无法找到CNode,但节点连接成功后可以通过nodes(connected)获取到。

下面讲述如何在Windows下使用CNode与Erlang通讯(demo下载)

一、前期准备

1、下载erlang二进制安装包,这里以 R16B02 为例。

2、安装erlang后,到安装目录取erlang头文件和静态库包:

erl头文件:erl5.10.3\lib\erl_interface-3.7.14\include\

erl静态库:erl5.10.3\lib\erl_interface-3.7.14\lib,静态库这里只用到ei_md.lib和erl_interface_md.lib

二、新建c项目

1、新建一个空项目,命名为cnode

2、把include文件夹和lib文件夹复制到工程目录下

3、修改项目属性,字符集选使用 Unicode 字符集

4、修改项目属性,VC++ 目录的包含目录添加“include”,库添加“lib”

5、工程新增文件cnode.c,保存以下代码:

#include <stdio.h>
#include <sys/types.h>

#ifdef _WIN32  
#   define __WIN32__
#   include <WinSock2.h>  
#	pragma comment(lib, "ws2_32.lib")
#	pragma comment(lib, "ei_md.lib")
#	pragma comment(lib, "erl_interface_md.lib")
#	pragma comment(linker, "/NODEFAULTLIB:MSVCRTD.LIB")
#endif 

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

#define BUFSIZE 1000
#define PORT 8088

int foo(int x) {
  return x+1;
}

int bar(int y) {
  return y*2;
}

int main(int argc, char **argv) {
  struct in_addr addr;                     /* 32-bit IP number of host */
  int port;                                /* Listen port number */
  int listen;                              /* Listen socket */
  int fd;                                  /* fd to Erlang node */
  ErlConnect conn;                         /* Connection data */

  int loop = 1;                            /* Loop flag */
  int got;                                 /* Result of receive */
  unsigned char buf[BUFSIZE];              /* Buffer for incoming message */
  ErlMessage emsg;                         /* Incoming message */

  ETERM *fromp, *tuplep, *fnp, *argp, *resp;
  int res;

#ifdef _WIN32  
  //初始化winsock服务  
  WSADATA wsaData;  
  WSAStartup(MAKEWORD(2,2), &wsaData);  
#endif 

  port = PORT;

  erl_init(NULL, 0);

  addr.s_addr = inet_addr("127.0.0.1");
  if (erl_connect_xinit("idril", "cnode", "cnode@127.0.0.1",
    &addr, "secretcookie", 0) == -1)
    erl_err_quit("erl_connect_xinit");

  /* Make a listen socket */
  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) {
      /* ignore */
    } 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);
      }
    }
  }
}


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;
}

编译项目后,会生成cnode.exe的程序。这里要先启动erlang epmd服务,再跑这个程序。原因是erlang节点之间的通讯要依赖一个底层的端口映射服务epmd,这个模块的主要功能是提供通过相互通过name来识别机器的机制。

7、启动erlang epmd服务

可以先在本机启动一个erlang节点,erlang会自动起这个epmd服务。demo中也带了这个程序epmd.exe,手动执行即可

C:\>erl -name e1@127.0.0.1 -setcookie secretcookie 
Eshell V5.10.3 (abort with ^G) 
(e1@127.0.0.1)1> net_kernel:connect('cnode@127.0.0.1'). 
true 
(e1@127.0.0.1)2> {any, 'cnode@127.0.0.1'} ! {call, self(), {bar, 3}}. 
{call,<0.36.0>,{bar,3}} 
(e1@127.0.0.1)3> flush(). 
Shell got {cnode,6} 
ok 
(e1@127.0.0.1)4> {any, 'cnode@127.0.0.1'} ! {call, self(), {foo, 3}}. 
{call,<0.36.0>,{foo,3}} 
(e1@127.0.0.1)5> flush(). 
Shell got {cnode,4} 
ok

完整demo下载: http://download.csdn.net/detail/cwqcwk1/8126053

之前写过nif的例子,有网友表示demo无法跑起来,这里总结其中的情况:

1、demo的erlang版本是R16B02,必须使用这个版本的erlang,否则会有问题。 下载页

2、我的编译器是vs2010,必须安装有vs2010以上的编译器。如果没有安装,只是想运行这个demo需要安装 vc2010运行库

3、epmd服务未启动,demo中自带了epmd.exe,双击即可。或者先在本机启动一个erlang节点,erlang会自动起这个服务。‘

4、其他erlang节点之间无法通讯的原因,还有cookie、名字问题,参考这篇文章

上面讲的是CNode做服务端节点的例子,下面也贴下CNode做客户端节点的代码:

int main(int argc, char **argv) {
  int fd;                                  /* fd to Erlang node */

  int loop = 1;                            /* Loop flag */
  int got;                                 /* Result of receive */
  unsigned char buf[BUFSIZE];              /* Buffer for incoming message */
  ErlMessage emsg;                         /* Incoming message */

  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) {
    /* ignore */
    } 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);
      }
    }
  }
}

代码就参照上面的方法编译了


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值