CNode指南

好久不發文章,因爲工作太忙。這個東西就湊湊數吧。各位見諒。

下一篇可能會介紹port,因爲工作中用的比較多.

 

 

CNode用戶指南

Author: litaocheng
Mail: [email protected]
Date: 2009.7.8

1   概述

利用Erlang的 erl_interface ,我們可以構建自己的Erlang "Node",我們稱爲 "CNode ",erl_interface實現了基本的節點連接,以及消息發送接收,以及Erlang Term 的構建解析.

使用CNode,可以完成很多使用port完成的功能,區別是你的應用是一個c程序,而不需要 一個erlang應用.

CNode是hidden node,在erlang的 nodes() 結果中,無法看到CNode(可以通過nodes(connected)獲取)

2   CNode 相關API

2.1   建立連接

erl_init(NULL, 0) 首先進行erlang環境的初始化,主要是內存的初始化. 隨後進行C node的初始化,假如我們的C node name爲short name, 調用 erl_connect_init(1, "secretcookie", 0) 則創建了一個名稱爲 c1 的C node. 如果要創建一個long name的C node,那麼需要調用 erl_connect_xinit("idril", "cnode", "[email protected]", &addr, "secretcookie", 0); .

C node可以表現爲一個client,也可以表現爲一個srever.

  • 作爲client,調用 fd = erl_connect("e1@idril") 連接其他節點
  • 作爲server
    1. bind(),並listen一個本地端口
    2. 調用 erl_publish(port); 聲明綁定的端口
    3. 調用 fd = erl_accept(listen, &conn); 等待client連接

2.2   發送接收消息

通過調用 erl_receive_msg() ,C node可以從Erlang節點接收消息.其通過建立連接時的fd接收消息. 接收的消息被存放在名爲ErlMessage的結構中,ErlMessage的type字段表明接收的消息的類型,如 ERL_REG_SEND 表明 Erlang節點向C node中的某個registered process發送消息.ErlMessage的類型爲ETerm的msg字段保存具體 的消息內容.

erl_receive_msg 可能會返回 ERL_ERROR (錯誤發生), ERR_TICK (節點心跳檢測),還有和process link/unlink,以及 exit信號相關的返回值,需要我們注意.

代碼片段如下:

while (loop) {
    got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);
     if (got == ERL_TICK) {
             /* ignore */
     } else if (got == ERL_ERROR) {
     loop = 0; /* exit while loop */
     } else {
         if (emsg.type == ERL_REG_SEND) {

消息體爲ETerm,可以使用erl_interface中的相關API進行操作.我們的示例中,消息體爲 一個三元tuple,第二個元素爲發送者的pid,第三個元素爲tuple:{Function,Arg}. 計算的結果,通過 erl_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));
}

最後,所有創建ETerm相關內存,需要釋放:

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

resp = erl_format("{cnode, ~i}", res);
erl_send(fd, fromp, resp);

2.3   Erlang client

complex3:

-module(complex3).
-export([foo/1, bar/1]).

foo(X) ->
   call_cnode({foo, X}).
bar(Y) ->
   call_cnode({bar, Y}).

call_cnode(Msg) ->
   {any, c1@litao} ! {call, self(), Msg},
   receive
       {cnode, Result} ->
           Result
   end.

3   編譯運行

編譯C node:

$ gcc -o cserver -I/usr/local/lib/erlang/lib/erl_interface-3.6.2/include \
 -L/usr/local/lib/erlang/lib/erl_interface-3.6.2/lib \
 complex.c cnode_s.c -g -rdynamic -lerl_interface -lei  -lnsl -lpthread

啓動epmd:

epmd -d -d #以debug方式啓動

啓動cserver:

./cserver 2342

至此C node啓動完成,其綁定的本地端口爲2342

啓動client:

erl -sname t1

設置 c1@litao 的cookie,連接c1節點:

(t1@litao)6> erlang:set_cookie('c1@litao', 'secretcookie').
true
(t1@litao)7> net_kernel:connect_node('c1@litao').
true
(t1@litao)8> nodes(connected).
[c1@litao]

執行調用:

(t1@litao)12> complex3:foo(3).
4

4   完整代碼

cnode_s.c:

/* 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;                                /* 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;

 port = atoi(argv[1]);

 erl_init(NULL, 0);

 if (erl_connect_init(1, "secretcookie", 0) == -1)
   erl_err_quit("erl_connect_init");

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


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

compex.c:

/* complex.c */

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

int bar(int y) {
 return y*2;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章