CNode 是利用 Erlang erl_interface 庫實現的偽裝成 Erlang 節點的 C 程序。與 NIF(Native Implemented Function)不同,CNode 運行在獨立 OS 進程,通過 TCP/Socket 與 Erlang VM 通訊,崩潰不會拖垮 Erlang VM,適合不可靠的第三方庫隔離、長時間運算卸載、硬件驅動封裝等場景。
安裝目錄\lib\erl_interface-3.7.14\include\ -> 複製到項目 include/
安裝目錄\lib\erl_interface-3.7.14\lib\ -> 複製 ei_md.lib, erl_interface_md.lib 到項目 lib/
include,庫目錄添加 libws2_32.lib; ei_md.lib; erl_interface_md.libMSVCRTD.lib(避免 CRT 衝突)// 初始化 WinSock & erl_interface
WSAStartup(MAKEWORD(2,2), &wsaData);
erl_init(NULL, 0);
// 發布節點名、Cookie、IP、端口
struct in_addr addr; addr.s_addr = inet_addr("127.0.0.1");
erl_connect_xinit("idril", "cnode", "cnode@127.0.0.1", &addr, "secretcookie", 0);
// 建立監聽 Socket
int listen = my_listen(PORT); // bind/listen 封裝
erl_publish(PORT); // 向 epmd 註冊
// 接受 Erlang 節點連接
ErlConnect conn;
int fd = erl_accept(listen, &conn); // 陻塞等待連接
// 消息循環
while (loop) {
int got = erl_receive_msg(fd, buf, BUFSIZE, &emsg);
if (got == ERL_REG_SEND) { // {RegName, From, Msg} 格式
// 解碼 Term:{call, FromPid, {FuncName, Arg}}
ETERM* fromp = erl_element(2, emsg.msg);
ETERM* tuplep = erl_element(3, emsg.msg);
ETERM* fnp = erl_element(1, tuplep);
ETERM* argp = erl_element(2, tuplep);
// 業務邏輯分發
int res = (strcmp(ERL_ATOM_PTR(fnp), "foo")==0) ? foo(ERL_INT_VALUE(argp))
: bar(ERL_INT_VALUE(argp));
// 編碼回覆 {cnode, Result}
ETERM* resp = erl_format("{cnode, ~i}", res);
erl_send(fd, fromp, resp);
// 釋放所有 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);
}
}
% 啟動 Erlang 節點
erl -name e1@127.0.0.1 -setcookie secretcookie
% 連接 CNode
net_kernel:connect('cnode@127.0.0.1'). % -> true
% 發送異步請求
{any, 'cnode@127.0.0.1'} ! {call, self(), {bar, 3}}.
flush(). % 收到 {cnode,6}
主動連接已知 Erlang 節點,無需 erl_publish/erl_accept:
erl_connect_init(1, "secretcookie", 0);
int fd = erl_connect("e1@127.0.0.1"); // 主動連接
// 後續消息循環同服務端
| 現象 | 原因 | 對策 |
|---|---|---|
| 編譯報錯找不到符號 | Erlang 版本不匹配(必須用 R16B02 對應的 erl_interface) | 嚴格對應版本,或升級代碼適配新版 API |
| erl_connect 返回 -1 | epmd 未啟動、Cookie 不一致、節點名解析失敗 | 啟動 epmd.exe 或先起一個 Erlang 節點;檢查 -name/-setcookie |
| 收到 ERL_ERROR 導致循環退出 | 網絡斷開、對端關閉、Term 解碼失敗 | 加入重連邏輯;檢查 erl_format 格式字符串與參數匹配 |
| 內存洩漏 | 未調用 erl_free_term 釋放解碼出的 ETerm* | 每個 erl_element/erl_format 產生的指針必須成對 free |
nodes(),僅在 nodes(connected) 可見,安全性更高。erl_mk_binary 構建二進制 Term,零拷貝傳遞大數據塊。erl_receive_msg,或用 erl_select 整合進事件循環。CNode 是 Erlang 生態與 C/C++ 系統互操作的強大橋樑。掌握 erl_interface 生命週期管理、Term 編解碼規範、epmd 註冊機制,即可構建高可靠、高性能的異構集群。建議將通訊層封裝為庫,業務邏輯純 C 實現,單元測試覆蓋編解碼邊界條件。