首页 > 代码库 > Erlang与C构建的节点通讯

Erlang与C构建的节点通讯

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

Cnode与erl_interface

想用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)获取到。

CNode与Erlang通讯

下面讲述如何在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,保存以下代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include <sys/types.h>  
  3.   
  4. #ifdef _WIN32    
  5. #   define __WIN32__  
  6. #   include <WinSock2.h>    
  7. #   pragma comment(lib, "ws2_32.lib")  
  8. #   pragma comment(lib, "ei_md.lib")  
  9. #   pragma comment(lib, "erl_interface_md.lib")  
  10. #   pragma comment(linker, "/NODEFAULTLIB:MSVCRTD.LIB")  
  11. #endif   
  12.   
  13. #include "erl_interface.h"  
  14. #include "ei.h"  
  15.   
  16. #define BUFSIZE 1000  
  17. #define PORT 8088  
  18.   
  19. int foo(int x) {  
  20.   return x+1;  
  21. }  
  22.   
  23. int bar(int y) {  
  24.   return y*2;  
  25. }  
  26.   
  27. int main(int argc, char **argv) {  
  28.     struct in_addr addr;                     /* 32-bit IP number of host */  
  29.     int port;                                /* Listen port number */  
  30.     int listen;                              /* Listen socket */  
  31.     int fd;                                  /* fd to Erlang node */  
  32.     ErlConnect conn;                         /* Connection data */  
  33.   
  34.     int loop = 1;                            /* Loop flag */  
  35.     int got;                                 /* Result of receive */  
  36.     unsigned char buf[BUFSIZE];              /* Buffer for incoming message */  
  37.     ErlMessage emsg;                         /* Incoming message */  
  38.   
  39.     ETERM *fromp, *tuplep, *fnp, *argp, *resp;  
  40.     int res;  
  41.   
  42. #ifdef _WIN32    
  43.     //初始化winsock服务    
  44.     WSADATA wsaData;    
  45.     WSAStartup(MAKEWORD(2,2), &wsaData);    
  46. #endif   
  47.   
  48.     port = PORT;  
  49.   
  50.     erl_init(NULL, 0);  
  51.   
  52.     addr.s_addr = inet_addr("127.0.0.1");  
  53.     if (erl_connect_xinit("idril", "cnode", "cnode@127.0.0.1",  
  54.         &addr, "secretcookie", 0) == -1)  
  55.         erl_err_quit("erl_connect_xinit");  
  56.   
  57.     /* Make a listen socket */  
  58.     if ((listen = my_listen(port)) <= 0)  
  59.         erl_err_quit("my_listen");  
  60.   
  61.     if (erl_publish(port) == -1)  
  62.         erl_err_quit("erl_publish");  
  63.   
  64.     if ((fd = erl_accept(listen, &conn)) == ERL_ERROR)  
  65.         erl_err_quit("erl_accept");  
  66.     fprintf(stderr, "Connected to %s\n\r", conn.nodename);  
  67.   
  68.     while (loop) {  
  69.   
  70.         got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);  
  71.         if (got == ERL_TICK) {  
  72.             /* ignore */  
  73.         } else if (got == ERL_ERROR) {  
  74.             loop = 0;  
  75.         } else {  
  76.   
  77.             if (emsg.type == ERL_REG_SEND) {  
  78.                 fromp = erl_element(2, emsg.msg);  
  79.                 tuplep = erl_element(3, emsg.msg);  
  80.                 fnp = erl_element(1, tuplep);  
  81.                 argp = erl_element(2, tuplep);  
  82.   
  83.                 if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {  
  84.                     res = foo(ERL_INT_VALUE(argp));  
  85.                 } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {  
  86.                     res = bar(ERL_INT_VALUE(argp));  
  87.                 }  
  88.   
  89.                 resp = erl_format("{cnode, ~i}", res);  
  90.                 erl_send(fd, fromp, resp);  
  91.   
  92.                 erl_free_term(emsg.from); erl_free_term(emsg.msg);  
  93.                 erl_free_term(fromp); erl_free_term(tuplep);  
  94.                 erl_free_term(fnp); erl_free_term(argp);  
  95.                 erl_free_term(resp);  
  96.             }  
  97.         }  
  98.     }  
  99. }  
  100.   
  101.   
  102. int my_listen(int port) {  
  103.     int listen_fd;  
  104.     struct sockaddr_in addr;  
  105.     int on = 1;  
  106.   
  107.     if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)  
  108.         return (-1);  
  109.   
  110.     setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));  
  111.   
  112.     memset((void*) &addr, 0, (size_t) sizeof(addr));  
  113.     addr.sin_family = AF_INET;  
  114.     addr.sin_port = htons(port);  
  115.     addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  116.   
  117.     if (bind(listen_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)  
  118.         return (-1);  
  119.   
  120.     listen(listen_fd, 5);  
  121.     return listen_fd;  
  122. }  

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

7、启动erlang epmd服务

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

8、运行erlang节点

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. C:\>erl -name e1@127.0.0.1 -setcookie secretcookie   
  2. Eshell V5.10.3 (abort with ^G)   
  3. (e1@127.0.0.1)1> net_kernel:connect(‘cnode@127.0.0.1‘).   
  4. true   
  5. (e1@127.0.0.1)2> {any, ‘cnode@127.0.0.1‘} ! {call, self(), {bar, 3}}.   
  6. {call,<0.36.0>,{bar,3}}   
  7. (e1@127.0.0.1)3> flush().   
  8. Shell got {cnode,6}   
  9. ok   
  10. (e1@127.0.0.1)4> {any, ‘cnode@127.0.0.1‘} ! {call, self(), {foo, 3}}.   
  11. {call,<0.36.0>,{foo,3}}   
  12. (e1@127.0.0.1)5> flush().   
  13. Shell got {cnode,4}   
  14. ok  

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

demo无法编译或无法起来的原因?

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

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

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

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

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

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

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int main(int argc, char **argv) {  
  2.     int fd;                                  /* fd to Erlang node */  
  3.   
  4.     int loop = 1;                            /* Loop flag */  
  5.     int got;                                 /* Result of receive */  
  6.     unsigned char buf[BUFSIZE];              /* Buffer for incoming message */  
  7.     ErlMessage emsg;                         /* Incoming message */  
  8.   
  9.     ETERM *fromp, *tuplep, *fnp, *argp, *resp;  
  10.     int res;  
  11.     
  12.     erl_init(NULL, 0);  
  13.   
  14.     if (erl_connect_init(1, "secretcookie", 0) == -1)  
  15.         erl_err_quit("erl_connect_init");  
  16.   
  17.     if ((fd = erl_connect("e1@idril")) < 0)  
  18.         erl_err_quit("erl_connect");  
  19.     fprintf(stderr, "Connected to ei@idril\n\r");  
  20.   
  21.     while (loop) {  
  22.   
  23.         got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);  
  24.         if (got == ERL_TICK) {  
  25.         /* ignore */  
  26.         } else if (got == ERL_ERROR) {  
  27.             loop = 0;  
  28.         } else {  
  29.   
  30.             if (emsg.type == ERL_REG_SEND) {  
  31.                 fromp = erl_element(2, emsg.msg);  
  32.                 tuplep = erl_element(3, emsg.msg);  
  33.                 fnp = erl_element(1, tuplep);  
  34.                 argp = erl_element(2, tuplep);  
  35.   
  36.                 if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {  
  37.                     res = foo(ERL_INT_VALUE(argp));  
  38.                 } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {  
  39.                     res = bar(ERL_INT_VALUE(argp));  
  40.                 }  
  41.   
  42.                 resp = erl_format("{cnode, ~i}", res);  
  43.                 erl_send(fd, fromp, resp);  
  44.   
  45.                 erl_free_term(emsg.from); erl_free_term(emsg.msg);  
  46.                 erl_free_term(fromp); erl_free_term(tuplep);  
  47.                 erl_free_term(fnp); erl_free_term(argp);  
  48.                 erl_free_term(resp);  
  49.             }  
  50.         }  
  51.     }  
  52. }  

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

最后,如果对erlang有什么问题或者对某些技术感兴趣的,都可以回帖告诉我。我看到就会找时间研究一下,做一个分享或讨论。

 

Erlang与C构建的节点通讯