ei_connect
C库
ei_connect
库摘要
与分布式Erlang进行通信。
描述
该模块使C程序能够使用Erlang分布在TCP / IP上与Erlang节点进行通信。
一个C节点对Erlang来说是一个隐藏节点。 也就是说,知道C节点名称的Erlang进程可以以正常的方式与它进行通信,但ERTS中erlang:nodes / 0提供的清单中并未显示节点名称。
环境变量ERL_EPMD_PORT
可用于指示C节点属于哪个逻辑群集。
超时函数
大多数函数都以附加到函数名称后缀_tmo的版本显示。 这些函数需要额外的参数,以毫秒为单位超时。 语义是这样的:对于操作中涉及的每个通信原语,如果原语在指定的时间内没有完成,则该函数返回一个错误,并且erl_errno被设置为ETIMEDOUT。 通信原语是指在套接字上进行的操作,如connect,accept,recv或send。
显然,超时是为了实现容错,而不是坚持实时的承诺。这些_tmo
函数用于检测无响应的对等体并避免对套接字操作的阻塞。
超时值0
(零)意味着超时被禁用。因此调用_tmo
带有最后一个参数的函数与0
调用没有_tmo
后缀的函数相同。
与所有其他以ei_开头的函数一样,您不希望自己将程序中的套接字置于非阻塞模式。 每个非阻塞模式的使用都嵌入到超时功能中。 操作完成后,套接字将始终回到阻塞模式(不管结果如何)。 为避免出现问题,请单独保留套接字选项。 ei处理任何需要修改的套接字选项。
在所有其他的意义上,_tmo
函数继承了所有返回值和来自没有_tmo
后缀的函数的语义。
输出
struct hostent *ei_gethostbyaddr(const char *addr, int len, int type)struct hostent *ei_gethostbyaddr_r(const char *addr, int length, int type, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)struct hostent *ei_gethostbyname(const char *name)struct hostent *ei_gethostbyname_r(const char *name, struct hostent *hostp, char *buffer, int buflen, int *h_errnop)
一些常见的名称查找函数的方便函数。
int ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp)
由服务器进程使用来接受来自客户端进程的连接。
ec
是C节点结构。
listensock
是listen()
之前被调用的开放式套接字描述符。
conp是一个指向ErlConnect结构的指针,描述如下:
typedef struct {char ipadr4; char nodenameMAXNODELEN; } ErlConnect; 成功时,conp将填入连接客户端的地址和节点名称,并返回文件描述符。 失败时,返回ERL_ERROR并将erl_errno设置为EIO.int ei_accept_tmo(ei_cnode * ec,int listensock,ErlConnect * conp,unsigned timeout_ms)与具有可选超时参数的ei_accept等效,请参阅本手册开头的说明 page.int ei_connect(ei_cnode * ec,char * nodename)int ei_xconnect(ei_cnode * ec,Erl_IpAddr adr,char * alivename)建立到Erlang节点的连接..ei_xconnect()需要远程主机的IP地址并且存活 要指定的远程节点的名称。 ei_connect()提供了一个替代接口,并根据提供的节点名称确定信息。
addr
远程主机的32位IP地址。
alive
远程节点的别名。
node
远程节点的名称。
这些函数在成功时返回一个打开的文件描述符,或者返回一个负值,指示发生错误。在后一种情况下,他们设置了其中一个erl_errno
:
EHOSTUNREACH远程主机节点无法访问。 ENOMEM没有更多的内存可用。 EIO I / O错误。
此外,来自套接字(2)和连接(2)系统调用的errno值可能会传播到erl_errno中。
例子:
#define NODE "madonna@chivas.du.etx.ericsson.se"
#define ALIVE "madonna"
#define IP_ADDR "150.236.14.75"
/*** Variant 1 ***/
int fd = ei_connect(&ec, NODE);
/*** Variant 2 ***/
struct in_addr addr;
addr.s_addr = inet_addr(IP_ADDR);
fd = ei_xconnect(&ec, &addr, ALIVE);
int ei_connect_init(ei_cnode* ec, const char* this_node_name, const char *cookie, short creation)int ei_connect_xinit(ei_cnode* ec, const char *thishostname, const char *thisalivename, const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, short creation)
初始化ec
结构,以标识服务器的节点名称和Cookie。其中一个必须在使用其他ei_cnode
类型的函数或与另一个节点的连接关联的文件描述符之前被调用。
ec
包含有关C节点的信息的结构。它用于其他ei
连接和接收数据的功能。
this_node_name
是过程的注册名称('@'之前的名称)。
cookie
是节点的cookie。
creation
标识C节点的特定实例。它可以帮助防止节点接收发送到具有相同注册名称的早期进程的消息。
thishostname
是我们正在运行的机器的名称。如果要使用长名称,则要完全限定(即,durin.erix.ericsson.se
而不是durin
)。
thisalivename
进程的注册名称。
thisnodename
是节点的全名,即einode@durin
。
thispaddr
主机的IP地址。
充当服务器的C节点在调用时被分配一个创建号。ei_publish()
...
只需关闭套接字即可关闭连接。有关如何正常关闭套接字(关闭前有外出数据包)的信息,请参阅相关的系统文档。
这些函数返回一个负值,指示发生错误。
例1:
int n = 0;
struct in_addr addr;
ei_cnode ec;
addr.s_addr = inet_addr("150.236.14.75");
if (ei_connect_xinit(&ec,
"chivas",
"madonna",
"madonna@chivas.du.etx.ericsson.se",
&addr;
"cookie...",
n++) < 0) {
fprintf(stderr,"ERROR when initializing: %d",erl_errno);
exit(-1);
}
例2:
if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) {
fprintf(stderr,"ERROR when initializing: %d",erl_errno);
exit(-1);
}
int ei_connect_tmo(ei_cnode* ec, char *nodename, unsigned timeout_ms)int ei_xconnect_tmo(ei_cnode* ec, Erl_IpAddr adr, char *alivename, unsigned timeout_ms)
相当于ei_connect
和ei_xconnect
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_get_tracelevel(void)void ei_set_tracelevel(int level)
用于设置发行版的跟踪。这些级别是不同的详细程度。更高的层次意味着更多的信息。另见部分Debug Information
...
这些函数不是线程安全的。
int ei_publish(ei_cnode *ec, int port)
由服务器进程用于向本地名称服务器EPMD注册,从而允许其他进程使用注册的名称发送消息。在调用这些函数之前,进程应该调用bind()
和listen()
在打开的插座上。
ec
是C节点结构。
port
要注册的本地名称,并且与先前绑定到套接字的端口号相同。
- addr是本地主机的32位IP地址。要取消注册EPMD,只需关闭返回的描述符即可。 不要使用ei_unpublish(),但不管怎样都不推荐使用。如果成功,该函数将返回一个描述符,将调用进程连接到EPMD。 失败时,返回-1并将erl_errno设置为EIO。同时,可以将来自套接字(2)和连接(2)系统调用的errno值传播到erl_errno.int ei_publish_tmo(ei_cnode * ec,int port,unsigned timeout_ms)Equivalent 使用可选的超时参数进行ei_publish,请参阅本手册开始部分的描述page.int ei_receive(int fd,unsigned char * bufp,int bufsize)接收由Erlang外部格式的字节序列组成的消息。
fd
是一个Erlang连接的开放描述符。它是从以前ei_connect
或之后获得的ei_accept
。
bufp
是一个足够大的缓冲区,足以容纳所需的消息。
bufsize
表示bufp
...
如果tick发生,即连接另一端的Erlang节点已轮询此节点以查看它是否仍然活着,函数返回ERL_TICK
并且没有消息被放置在缓冲区中。还有,erl_errno
设置为EAGAIN
...
成功后,消息将放置在指定的缓冲区中,该函数返回实际读取的字节数。失败时,函数返回其中一个ERL_ERROR
和集erl_errno
:
EAGAIN
临时错误:重试。EMSGSIZE
缓冲区太小了。EIO
I/O错误
int ei_receive_encoded(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen)
为了与接口编译器生成的代码以及在同一应用程序中的以下示例代码兼容,保留了此函数。
本质上,该函数执行的操作与ei_xreceive_msg相同,但不是使用ei_x_buff,而是需要一个指向字符指针(mbufp)的指针,其中字符指针指向由malloc分配的内存区域。 参数bufsz是一个指向包含内存区域的确切大小(以字节为单位)的整数的指针。 该函数可能会重新分配内存区域,并将在这种情况下将新大小放入* bufsz并更新* mbufp。
返回erlang_msg * msg的ERL_TICK或msgtype字段。 消息的长度放在* msglen中。 出错时返回值<0。
为了便于阅读,建议尽可能使用ei_xreceive_msg。 但是,该功能将保留在界面中以便兼容,并且在将来的发行版中不会在未事先通知的情况下将其删除。
int ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg, int *msglen, unsigned timeout_ms)
相当于ei_receive_encoded
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_receive_msg(int fd, erlang_msg* msg, ei_x_buff* x)int ei_xreceive_msg(int fd, erlang_msg* msg, ei_x_buff* x)
中的缓冲区接收消息。x
...ei_xreceive_msg
允许缓冲区进入x
成长,但是ei_receive_msg
中的消息大于预先分配的缓冲区时失败。x
...
fd
是Erlang连接的打开描述符。
msg
是指向erlang_msg
结构,并包含有关接收到的消息的信息。
x
是ei_x_new
从中获得的缓冲。
成功时,函数返回ERL_MSG
且msg
初始化结构体。erlang_msg
定义如下:
typedef struct {
long msgtype;
erlang_pid from;
erlang_pid to;
char toname[MAXATOMLEN+1];
char cookie[MAXATOMLEN+1];
erlang_trace token;
} erlang_msg;
msgtype
标识消息的类型,它是以下内容之一:
ERL_SEND
表示发生了普通的发送操作。msg->to
包含收件人的pid(C节点)。
ERL_REG_SEND
发生了已注册的发送操作。msg->from
包含发件人的PID。
ERL_LINK
或ERL_UNLINK
msg->to
和msg->from
包含链接或取消链接的发件人和收件人的PIDS。
ERL_EXIT
指示断开链接。msg->to
和msg->from
包含链接进程的PIDS。
返回值与ei_receive
...
int ei_receive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned imeout_ms)int ei_xreceive_msg_tmo(int fd, erlang_msg* msg, ei_x_buff* x, unsigned timeout_ms)
相当于ei_receive_msg
和ei_xreceive_msg
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms)
相当于ei_receive
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_reg_send(ei_cnode* ec, int fd, char* server_name, char* buf, int len)
向注册进程发送Erlang术语。
fd
是Erlang连接的打开描述符。
server_name
指定收件人的注册名称。
buf
包含二进制格式术语的缓冲区。
len
消息的长度(以字节为单位)。
如果成功则返回0,否则返回-1。 在后一种情况下,它将erl_errno设置为EIO。
例子:
将原子“ok”发送给进程“worker”:
ei_x_buff x;
ei_x_new_with_version(&x);
ei_x_encode_atom(&x, "ok");
if (ei_reg_send(&ec, fd, x.buff, x.index) < 0)
handle_error();
int ei_reg_send_tmo(ei_cnode* ec, int fd, char* server_name, char* buf, int len, unsigned timeout_ms)
相当于ei_reg_send
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_rpc(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen, ei_x_buff *x)int ei_rpc_to(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf, int argbuflen)int ei_rpc_from(ei_cnode *ec, int fd, int timeout, erlang_msg *msg, ei_x_buff *x)
支持在远程节点上调用Erlang函数。ei_rpc_to()
向远程节点发送RPC请求,并且ei_rpc_from()
接收这样一个呼叫的结果。ei_rpc()
通过发送RPC请求并等待结果来组合这两个函数的功能。另见rpc:call/4
在内核。
ec
之前通过调用ei_connect_init()
或ei_connect_xinit()
...
fd
是Erlang连接的打开描述符。
timeout
是等待结果的最长时间(以毫秒为单位)。指定ERL_NO_TIMEOUT
等待永久。ei_rpc()
答案无限等待,也就是说,调用永远不会超时。
mod
包含要在远程节点上运行的函数的模块的名称。
fun
要运行的函数的名称。
argbuf
是指向带有编码Erlang列表的缓冲区的指针,没有版本魔术号,包含要传递给函数的参数。
argbuflen
包含编码的Erlang列表的缓冲区的长度。
- msg是erlang_msg类型的结构,并包含有关所接收消息的信息。 有关erlang_msg格式的说明,请参阅ei_receive_msg。
- x指向接收结果的动态缓冲区。 对于ei_rpc(),这是没有版本幻数的结果。 对于ei_rpc_from(),结果返回一个版本幻数和一个2元组{rex,Reply} .ei_rpc()返回成功时的结果字节数,失败时返回-1。 ei_rpc_from()返回字节数,否则返回ERL_TICK,ERL_TIMEOUT和ERL_ERROR之一。 当失败时,所有三个函数将erl_errno设置为以下之一:EIO I / O错误。 ETIMEDOUT超时已过期。 EAGAIN临时错误:请重试。 例子:检查Erlang进程是否存在:
- int index = 0, is_alive; ei_x_buff args, result; ei_x_new(&result); ei_x_new(&args); ei_x_encode_list_header(&args, 1); ei_x_encode_pid(&args, &check_pid); ei_x_encode_empty_list(&args); if (ei_rpc(&ec, fd, "erlang", "is_process_alive", args.buff, args.index, &result) < 0) handle_error(); if (ei_decode_version(result.buff, &index) < 0 || ei_decode_bool(result.buff, &index, &is_alive) < 0) handle_error(); erlang_pid * ei_self(ei_cnode * ec)检索C节点的pid。 每个C节点在ei_send_reg,ei_rpc等中都有一个(伪)pid。 这包含在ec结构的一个字段中。 长时间直接从ei_cnode结构中获取此字段将是安全的。ei_send(int fd,erlang_pid * to,char * buf,int len)将Erlang项发送给进程。
fd
是Erlang连接的打开描述符。
to
消息的预定收件人的PID。
buf
包含二进制格式术语的缓冲区。
len
消息的长度(以字节为单位)。
如果成功则返回0,否则返回-1。 在后一种情况下,它将erl_errno设置为EIO。
int ei_send_encoded(int fd, erlang_pid* to, char* buf, int len)
完全一样ei_send
,为了向后兼容性保留替代名称。功能不未经事先通知而被移走。
int ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)
相当于ei_send_encoded
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_send_reg_encoded(int fd, const erlang_pid *from, const char *to, const char *buf, int len)
为了与接口编译器生成的代码以及在同一应用程序中的以下示例代码兼容,保留了此函数。
该函数作为ei_reg_send工作,但有一个例外。 它不需要将ei_cnode作为第一个参数,而是需要第二个参数erlang_pid,它将成为发送进程的进程标识符(在Erlang分发协议中)。
通过以下示例代码erlang_pid
可以从ei_cnode
结构中构建合适的代码:
ei_cnode ec;
erlang_pid *self;
int fd; /* the connection fd */
...
self = ei_self(&ec);
self->num = fd;
int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf, int len)
相当于ei_send_reg_encoded
使用可选的超时参数,请参阅本手册页开头的说明。
int ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms)
相当于ei_send
使用可选的超时参数,请参阅本手册页开头的说明。
const char *ei_thisnodename(ei_cnode *ec)const char *ei_thishostname(ei_cnode *ec)const char *ei_thisalivename(ei_cnode *ec)
可用于检索有关C节点的信息。这些值最初是用ei_connect_init()
或ei_connect_xinit()
...
这些函数只需从ec
结构。直接阅读字段可能会在很长一段时间内是安全的,因此这些功能并不是真正需要的。
int ei_unpublish(ei_cnode *ec)
可以由进程调用,以便在本地主机上从EPMD注销指定的节点。但是,这通常是不允许的,除非EPMD是用标志启动的。-relaxed_command_check
通常情况下并非如此。
若要注销已发布的节点,应关闭由ei_publish()
...
警告
此函数已被取消,并将在以后的版本中删除。
ec
要注销的节点的节点结构。
如果成功地从epmd取消节点注册,则该函数将返回0
.否则,-1
会被退回并且erl_errno
设置为EIO
...
int ei_unpublish_tmo(ei_cnode *ec, unsigned timeout_ms)
相当于ei_unpublish
使用可选的超时参数,请参阅本手册页开头的说明。
调试信息
如果连接尝试失败,可以检查以下内容:
erl_errno
...
- 正确的cookie被使用
- EPMD正在运行
- 另一端的远程Erlang节点正在运行与
ei
库相同版本的Erlang
- 环境变量
ERL_EPMD_PORT
设置为正确
可以通过设置跟踪级别来跟踪连接尝试,方法之一是使用ei_set_tracelevel
或者通过设置环境变量EI_TRACELEVEL
.跟踪级别有以下消息:
- 1:详细错误消息
- 2:以上信息和详细警告消息
- 3:上述消息和连接处理进度报告
- 4:上述信息和进度报告供交流
- 5:上述信息和数据转换进度报告
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com