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,保存以下代碼:
#include編譯項目後,會生成cnode.exe的程序。這裡要先啟動erlang epmd服務,再跑這個程序。原因是erlang節點之間的通訊要依賴一個底層的端口映射服務epmd,這個模塊的主要功能是提供通過相互通過name來識別機器的機制。#include #ifdef _WIN32 # define __WIN32__ # include # 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", "[email protected]", &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; }
7、啟動erlang epmd服務
可以先在本機啟動一個erlang節點,erlang會自動起這個epmd服務。demo中也帶了這個程序epmd.exe,手動執行即可
8、運行erlang節點
C:\>erl -name [email protected] -setcookie secretcookie Eshell V5.10.3 (abort with ^G) ([email protected])1> net_kernel:connect('[email protected]'). true ([email protected])2> {any, '[email protected]'} ! {call, self(), {bar, 3}}. {call,<0.36.0>,{bar,3}} ([email protected])3> flush(). Shell got {cnode,6} ok ([email protected])4> {any, '[email protected]'} ! {call, self(), {foo, 3}}. {call,<0.36.0>,{foo,3}} ([email protected])5> flush(). Shell got {cnode,4} 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、名字問題,參考這篇文章
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); } } } }
代碼就參照上面的方法編譯了
最後,如果對erlang有什麼問題或者對某些技術感興趣的,都可以回帖告訴我。我看到就會找時間研究一下,做一個分享或討論。
擴展閱讀:
erlang與java構建的節點通訊
erlang節點通訊例子及問題分析
erlang在windows下和虛擬機節點通信
Windows下使用NIF擴展Erlang方法
參考:http://blog.csdn.net/mycwq/article/details/40836273
http://www.erlang.org/doc/tutorial/cnode.html