查看源代码 ei_connect
与分布式 Erlang 通信。
描述
此模块使 C 程序能够使用基于 TCP/IP 的 Erlang 分布式通信与 Erlang 节点通信。
C 节点在 Erlang 中显示为隐藏节点。也就是说,知道 C 节点名称的 Erlang 进程可以正常方式与其通信,但是节点名称不会显示在 ERTS
中的 erlang:nodes/0
提供的列表中。
环境变量 ERL_EPMD_PORT
可用于指示 C 节点属于哪个逻辑集群。
超时函数
大多数函数都以附加后缀 _tmo
的版本出现。这些函数接受一个额外的参数,以毫秒为单位的超时时间。语义是这样的:对于操作中涉及的每个通信原语,如果原语在指定的时间内未完成,则函数返回错误,并且 erl_errno
设置为 ETIMEDOUT
。通信原语是指套接字上的操作,例如 connect
、accept
、recv
或 send
。
显然,超时用于实现容错,而不是为了保持硬实时承诺。_tmo
函数用于检测无响应的对等方,并避免阻塞套接字操作。
超时值 0
(零) 表示禁用超时。因此,使用最后一个参数 0
调用 _tmo
函数与调用没有 _tmo
后缀的函数相同。
与所有其他以 ei_
开头的函数一样,您不应该在程序中自己将套接字设置为非阻塞模式。非阻塞模式的每次使用都嵌入在超时函数内部。操作完成后,套接字将始终恢复为阻塞模式(无论结果如何)。为了避免问题,请不要更改套接字选项。ei
会处理任何需要修改的套接字选项。
在所有其他意义上,_tmo
函数继承了没有 _tmo
后缀的函数的所有返回值和语义。
用户提供的套接字实现
默认情况下,ei
提供一个 TCP/IPv4 套接字接口,该接口用于通信。但是,用户可以插入自己的 IPv4 套接字实现。例如,为了通过 TLS 进行通信。通过将 回调结构 传递给 ei_connect_init_ussi()
或 ei_connect_xinit_ussi()
,可以插入用户提供的套接字实现。
ei_socket_callbacks
结构中的所有回调在成功时应返回零;失败时返回 posix 错误代码。
listen
、accept
和 connect
回调的 addr
参数是指当前使用协议的相应地址结构。目前,ei
仅支持 IPv4。也就是说,此时 addr
始终指向 struct sockaddr_in
结构。
ei_socket_callbacks
结构在未来可能会扩大。所有未设置的字段需要清零。当前存在以下字段
flags
- 标志,用于通知ei
回调的行为。标志应按位或在一起。如果未设置任何标志,则flags
字段应包含0
。当前,支持的标志EI_SCLBK_FLG_FULL_IMPL
- 如果设置,则accept()
、connect()
、writev()
、write()
和read()
回调实现超时。超时时间以tmo
参数传入,单位为毫秒。请注意,这些回调的tmo
参数与ei
API 中的超时参数不同。零表示零超时。也就是说,轮询并立即超时,除非操作成功。EI_SCLBK_INF_TMO
(最大unsigned
)表示无限超时。调用回调时,文件描述符处于阻塞模式,并且回调返回时必须处于阻塞模式。如果未设置,
ei
将使用select()
实现超时,以确定何时调用回调以及何时超时。accept()
、connect()
、writev()
、write()
和read()
回调的tmo
参数应被忽略。回调可以在非阻塞模式下调用。不允许回调在阻塞和非阻塞模式之间更改。为了使其正常工作,select()
需要以与普通套接字原语交互相同的方式与使用的套接字原语交互。如果不是这种情况,则回调需要实现超时,并且应设置此标志。
将来可能会引入更多标志。
int (*socket)(void **ctx, void *setup_ctx)
- 创建一个套接字和一个套接字的上下文。成功后,它应将
*ctx
设置为指向已创建套接字的上下文。此上下文将传递给所有其他套接字回调。此函数将传递与之前的ei_connect_init_ussi()
或ei_connect_xinit_ussi()
调用相同的setup_context
。注意
在套接字的生命周期中,指针
*ctx
必须保持不变。也就是说,以后不能重新定位它。此回调是强制性的。
int (*close)(void *ctx)
- 关闭由ctx
标识的套接字并销毁上下文。此回调是强制性的。
int (*listen)(void *ctx, void *addr, int *len, int backlog)
- 将由ctx
标识的套接字绑定到本地接口,然后在该接口上侦听。addr
和len
参数都是输入和输出参数。调用时,addr
指向长度为*len
的地址结构,其中包含有关如何绑定套接字的信息。返回时,此回调应已使用有关套接字实际绑定方式的信息更新了addr
引用的结构。*len
应更新以反映更新后的*addr
的大小。backlog
标识侦听套接字的积压大小。此回调是强制性的。
int (*accept)(void **ctx, void *addr, int *len, unsigned tmo)
- 接受由*ctx
标识的侦听套接字上的连接。接受连接后,应为接受的连接创建一个新上下文,并且
*ctx
应更新为指向接受的连接的新上下文。调用时,addr
指向长度为*len
的未初始化地址结构。返回时,此回调应已使用有关客户端地址的信息更新此结构。*len
应更新以反映更新后的*addr
的大小。如果已设置
EI_SCLBK_FLG_FULL_IMPL
标志,则tmo
包含以毫秒为单位的超时时间。注意
在套接字的生命周期中,指针
*ctx
必须保持不变。也就是说,以后不能重新定位它。此回调是强制性的。
int (*connect)(void *ctx, void *addr, int len, unsigned tmo)
- 将由ctx
标识的套接字连接到由addr
标识的地址。调用时,
addr
指向长度为len
的地址结构,其中包含有关要连接的位置的信息。如果已设置
EI_SCLBK_FLG_FULL_IMPL
标志,则tmo
包含以毫秒为单位的超时时间。此回调是强制性的。
int (*writev)(void *ctx, const void *iov, long iovcnt, ssize_t *len, unsigned tmo)
- 在由ctx
标识的已连接套接字上写入数据。iov
指向长度为iovcnt
的struct iovec
结构数组,其中包含要写入套接字的数据。成功后,此回调应将*len
设置为成功写入套接字的字节数。如果已设置
EI_SCLBK_FLG_FULL_IMPL
标志,则tmo
包含以毫秒为单位的超时时间。此回调是可选的。如果未实现,则将
ei_socket_callbacks
结构中的writev
字段设置为NULL
。int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo)
- 在由ctx
标识的已连接套接字上写入数据。调用时,
buf
指向长度为*len
的缓冲区,其中包含要写入套接字的数据。成功后,此回调应将*len
设置为成功写入套接字的字节数。如果已设置
EI_SCLBK_FLG_FULL_IMPL
标志,则tmo
包含以毫秒为单位的超时时间。此回调是强制性的。
int (*read)(void *ctx, char *buf, ssize_t *len, unsigned tmo)
- 读取由ctx
标识的已连接套接字上的数据。buf
指向长度为*len
的缓冲区,应将读取的数据放入其中。成功后,此回调应将*len
更新为成功读取套接字的字节数。如果已设置
EI_SCLBK_FLG_FULL_IMPL
标志,则tmo
包含以毫秒为单位的超时时间。此回调是强制性的。
int (*handshake_packet_header_size)(void *ctx, int *sz)
- 通知在 Erlang 分布式握手期间要使用的握手数据包头大小。成功后,应将
*sz
设置为要使用的握手数据包头大小。有效值为2
和4
。Erlang TCP 分布使用2
的握手数据包大小,Erlang TLS 分布使用4
的握手数据包大小。此回调是强制性的。
int (*connect_handshake_complete)(void *ctx)
- 在本地启动的握手成功完成后调用。此回调是可选的。如果未实现,请将
ei_socket_callbacks
结构中的connect_handshake_complete
字段设置为NULL
。int (*accept_handshake_complete)(void *ctx)
- 当远程启动的握手成功完成时调用。此回调是可选的。如果未实现,请将
ei_socket_callbacks
结构中的accept_handshake_complete
字段设置为NULL
。int (*get_fd)(void *ctx, int *fd)
- 通知由ctx
标识的套接字使用的文件描述符。注意
在套接字的生命周期中,文件描述符必须保持不变。也就是说,对具有相同上下文的此回调的重复调用
应该
始终报告相同的文件描述符。文件描述符必须是真实的文件描述符。也就是说,在
close()
回调释放之前,不应有其他操作能够获取相同的文件描述符。此回调是强制性的。
数据类型
ei_cnode
- 表示 C 节点的 不透明数据类型。ei_cnode
结构通过调用ei_connect_init()
或类似函数来初始化。ei_socket_callbacks
typedef struct { int flags; int (*socket)(void **ctx, void *setup_ctx); int (*close)(void *ctx); int (*listen)(void *ctx, void *addr, int *len, int backlog); int (*accept)(void **ctx, void *addr, int *len, unsigned tmo); int (*connect)(void *ctx, void *addr, int len, unsigned tmo); int (*writev)(void *ctx, const void *iov, int iovcnt, ssize_t *len, unsigned tmo); int (*write)(void *ctx, const char *buf, ssize_t *len, unsigned tmo); int (*read)(void *ctx, char *buf, ssize_t *len, unsigned tmo); int (*handshake_packet_header_size)(void *ctx, int *sz); int (*connect_handshake_complete)(void *ctx); int (*accept_handshake_complete)(void *ctx); int (*get_fd)(void *ctx, int *fd); } ei_socket_callbacks;
用户提供的套接字实现的回调函数。 每个字段的文档可以在上面的用户提供的套接字实现部分中找到。
ErlConnect
typedef struct { char ipadr[4]; /* Ip v4 address in network byte order */ char nodename[MAXNODELEN]; } ErlConnect;
IP v4 地址和节点名称。
Erl_IpAddr
typedef struct { unsigned s_addr; /* Ip v4 address in network byte order */ } Erl_IpAddr;
IP v4 地址。
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;
有关通过
ei_receive_msg()
或类似函数接收的消息的信息。
ei_gethostbyaddr()
ei_gethostbyaddr_r()
ei_gethostbyname()
ei_gethostbyname_r()
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);
一些常用名称查找功能的便捷函数。
ei_accept()
int ei_accept(ei_cnode *ec, int listensock, ErlConnect *conp);
由服务器进程用于接受来自客户端进程的连接。
ec
是 C 节点结构。listensock
是一个已打开的套接字描述符,之前已在其上调用listen()
。conp
是指向ErlConnect
结构的指针。
成功时,conp
将填充连接客户端的地址和节点名称,并返回文件描述符。失败时,返回 ERL_ERROR
,并且 erl_errno
设置为 EIO
。
ei_accept_tmo()
int ei_accept_tmo(ei_cnode *ec, int listensock, ErlConnect *conp, unsigned timeout_ms);
等效于 ei_accept
,带有可选的超时参数,请参阅此手册页开头的说明。
ei_close_connection()
int ei_close_connection(int fd);
关闭之前打开的连接或侦听套接字。
自 OTP 21.3 起可用
ei_connect()
ei_xconnect()
ei_connect_host_port()
自 OTP 23.0 起可用
ei_xconnect_host_port()
int ei_connect(ei_cnode* ec, char *nodename);
int ei_xconnect(ei_cnode* ec, Erl_IpAddr adr, char *alivename);
int ei_connect_host_port(ei_cnode* ec, char *hostname, int port);
int ei_xconnect_host_port(ei_cnode* ec, Erl_IpAddr adr, int port);
建立与 Erlang 节点的连接。
ei_xconnect()
需要指定远程主机的 IP 地址和远程节点的活动名称。 ei_connect()
提供了一个替代接口,并从提供的节点名称中确定信息。即使在远程节点运行的主机上没有 EPMD 实例,ei_xconnect_host_port()
函数也提供了另一种替代方案。 ei_xconnect_host_port()
函数需要指定远程节点的 IP 地址和端口。 ei_connect_host_port()
函数是 ei_xconnect_host_port()
的替代方案,允许用户指定主机名而不是 IP 地址。
adr
是远程主机的 32 位 IP 地址。alive
是远程节点的活动名称。node
是远程节点的名称。port
是远程节点的端口号。
这些函数在成功时返回一个打开的文件描述符,或者返回一个指示发生错误的负值。在后一种情况下,它们将 erl_errno
设置为以下值之一
EHOSTUNREACH
- 无法访问远程主机node
。ENOMEM
- 没有更多可用内存。EIO
- I/O 错误。
此外,来自 socket(2)
和 connect(2)
系统调用的 errno
值可能会传播到 erl_errno
中。
示例
#define NODE "[email protected]"
#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);
自 OTP 23.0 起可用
ei_connect_init()
ei_connect_init_ussi()
自 OTP 21.3 起可用
ei_connect_xinit()
ei_connect_xinit_ussi()
int ei_connect_init(ei_cnode* ec, const char* this_node_name, const char *cookie, unsigned creation);
int ei_connect_init_ussi(ei_cnode* ec, const char* this_node_name, const char *cookie,
unsigned creation, ei_socket_callbacks *cbs, int cbs_sz, void *setup_context);
int ei_connect_xinit(ei_cnode* ec, const char *thishostname, const char *thisalivename,
const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, unsigned creation);
int ei_connect_xinit_ussi(ei_cnode* ec, const char *thishostname, const char *thisalivename,
const char *thisnodename, Erl_IpAddr thisipaddr, const char *cookie, unsigned creation,
ei_socket_callbacks *cbs, int cbs_sz, void *setup_context);
初始化 ec
结构,以标识服务器的节点名称和 Cookie。在对 ei_cnode
类型进行操作的其他函数或使用与另一个节点的连接关联的文件描述符之前,必须调用它们中的一个。
ec
是一个包含有关 C 节点信息的结构。它在其他ei
函数中用于连接和接收数据。this_node_name
是 C 节点的名称(完整节点名称中“@”之前的名称)。cookie
是节点的 Cookie。creation
标识 C 节点的特定实例。它可以帮助防止节点接收发送给具有相同注册名称的较早进程的消息。注意
creation
参数的类型在 OTP 25 中从short
(16 位) 更改为unsigned int
(32 位)。这不应该导致任何实际问题,除了可能出现编译器警告。thishostname
是我们正在运行的机器的名称。如果要使用长名称,它们必须是完全限定的(即,durin.erix.ericsson.se
而不是durin
)。thisalivename
是本地 C 节点的名称(完整节点名称中“@”之前的名称)。可以为NULL
(自 OTP 23 起)以从对等节点获取动态分配的名称。thisnodename
是本地 C 节点的完整名称,即mynode@myhost
。如果thisalivename
为NULL
,则可以为NULL
。thispaddr
是主机的 IP 地址。cbs
是指向实现替代套接字接口的回调结构的指针。cbs_sz
是cbs
指向的结构的大小。setup_context
是指向结构的指针,该结构将作为cbs
结构中的socket
回调的第二个参数传递。
当 C 节点作为服务器时,在调用 ei_publish()
时会分配一个创建编号。
通过简单地关闭套接字来关闭连接。有关如何正常关闭套接字(当关闭之前有传出数据包时)的信息,请参阅相关的系统文档。
这些函数返回一个指示发生错误的负值。
示例 1
unsigned 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",
"[email protected]",
&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);
}
自 OTP 21.3 起可用
ei_connect_tmo()
ei_xconnect_tmo()
ei_connect_host_port_tmo()
自 OTP 23.0 起可用
ei_xconnect_host_port_tmo()
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);
int ei_connect_host_port_tmo(ei_cnode* ec, char *hostname, int port, unsigned ms);
int ei_xconnect_host_port_tmo(ei_cnode* ec, Erl_IpAddr adr, int port, unsigned ms);
等效于 ei_connect
、ei_xconnect
、ei_connect_host_port
和 ei_xconnect_host_port
,带有可选的超时参数,请参阅此手册页开头的说明。
自 OTP 23.0 起可用
ei_get_tracelevel()
自 OTP R13B04 起可用
ei_set_tracelevel()
int ei_get_tracelevel(void);
void ei_set_tracelevel(int level);
用于设置分布的跟踪。级别是不同的详细级别。更高的级别意味着更多信息。另请参阅 调试信息 部分。
这些函数不是线程安全的。
自 OTP R13B04 起可用
ei_listen()
自 OTP 21.3 起可用
ei_xlisten()
int ei_listen(ei_cnode *ec, int *port, int backlog);
int ei_xlisten(ei_cnode *ec, Erl_IpAddr adr, int *port, int backlog);
由服务器进程用于设置侦听套接字,该套接字稍后可用于接受来自客户端进程的连接。
ec
是 C 节点结构。adr
是要绑定到的本地接口。port
是指向包含要绑定到的端口号的整数的指针。如果在调用ei_listen()
时*port
等于0
,则套接字将绑定到临时端口。成功时,ei_listen()
将更新*port
的值以实际绑定到的端口。backlog
是挂起连接的最大积压。
ei_listen
将创建一个套接字,绑定到由 adr
标识的本地接口上的端口(如果调用 ei_listen()
,则为所有本地接口),并将套接字标记为被动套接字(即,将用于接受传入连接的套接字)。
成功时,将返回一个文件描述符,该文件描述符可以在对 ei_accept()
的调用中使用。失败时,返回 ERL_ERROR
,并且 erl_errno
设置为 EIO
。
自 OTP 21.3 起可用
ei_make_pid()
int ei_make_pid(ei_cnode *ec, erlang_pid *pid);
在参数 pid
中创建一个新的进程标识符。此进程标识符是指驻留在由参数 ec
标识的 C 节点上的概念进程。成功时,返回 0
。失败时,返回 ERL_ERROR
,并设置 erl_errno
。
由 ec
标识的 C 节点必须已初始化,并且在调用 ei_make_pid()
之前必须已接收到一个名称。C 节点的初始化是通过调用 ei_connect_init()
或其相关函数完成的。如果名称是从对等节点动态分配的,则 C 节点也必须已连接。
自 OTP 23.0 起可用
ei_make_ref()
int ei_make_ref(ei_cnode *ec, erlang_ref *ref);
在参数 ref
中创建一个新的引用。此引用源自由参数 ec
标识的 C 节点。成功时返回 0
。失败时返回 ERL_ERROR
并设置 erl_errno
。
由 ec
标识的 C 节点必须已初始化,并且在调用 ei_make_ref()
之前必须已接收到一个名称。C 节点的初始化是通过调用 ei_connect_init()
或其相关函数完成的。如果名称是从对等节点动态分配的,则 C 节点也必须已连接。
自 OTP 23.0 起可用
ei_publish()
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
。
此外,来自 socket(2)
和 connect(2)
系统调用的 errno
值可能会传播到 erl_errno
中。
ei_publish_tmo()
int ei_publish_tmo(ei_cnode *ec, int port, unsigned timeout_ms);
等同于 ei_publish
,带有可选的超时参数,请参阅本手册页开头的描述。
ei_receive()
int ei_receive(int fd, unsigned char* bufp, int bufsize);
接收由 Erlang 外部格式的字节序列组成的消息。
fd
是到 Erlang 连接的打开描述符。它是从先前的ei_connect
或ei_accept
获取的。bufp
是一个足够大的缓冲区,可以容纳预期的消息。bufsize
指示bufp
的大小。
如果发生滴答,即 Erlang 节点在连接的另一端轮询此节点以查看它是否仍然存活,则该函数返回 ERL_TICK
,并且缓冲区中没有放置任何消息。此外,erl_errno
设置为 EAGAIN
。
成功时,消息将放置在指定的缓冲区中,并且该函数返回实际读取的字节数。失败时,该函数返回 ERL_ERROR
,并将 erl_errno
设置为以下之一
EAGAIN
- 临时错误:请重试。EMSGSIZE
- 缓冲区太小。EIO
- I/O 错误。
ei_receive_encoded()
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
。
返回 ERL_TICK
或 erlang_msg *msg
的 msgtype
字段。消息的长度放入 *msglen
中。如果发生错误,则返回的值 < 0
。
为了提高可读性,建议尽可能使用 ei_xreceive_msg
。但是,该函数将在接口中保留以实现兼容性,并且在没有事先通知的情况下不会在将来的版本中删除。
ei_receive_encoded_tmo()
int ei_receive_encoded_tmo(int fd, char **mbufp, int *bufsz, erlang_msg *msg,
int *msglen, unsigned timeout_ms);
等同于 ei_receive_encoded
,带有可选的超时参数,请参阅本手册页开头的描述。
ei_receive_msg()
ei_xreceive_msg()
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
中的缓冲区增长,但是如果消息大于 x
中预先分配的缓冲区,则 ei_receive_msg
会失败。
fd
是到 Erlang 连接的打开描述符。msg
是指向erlang_msg
结构的指针,并包含有关接收到的消息的信息。x
是从ei_x_new
获取的缓冲区。
成功时,该函数返回 ERL_MSG
,并且初始化 msg
结构。
msgtype
标识消息的类型,并且是以下类型之一
ERL_SEND
- 表示已发生普通发送操作。msg->to
包含接收者(C 节点)的 pid。ERL_REG_SEND
- 发生了注册发送操作。msg->from
包含发送者的 pid。ERL_LINK
或ERL_UNLINK
-msg->to
和msg->from
包含链接或取消链接的发送者和接收者的 pid。ERL_EXIT
- 表示链接已断开。msg->to
和msg->from
包含链接进程的 pid。
返回值与 ei_receive
的返回值相同。
ei_receive_msg_tmo()
ei_xreceive_msg_tmo()
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
,带有可选的超时参数,请参阅本手册页开头的描述。
ei_receive_tmo()
int ei_receive_tmo(int fd, unsigned char* bufp, int bufsize, unsigned timeout_ms);
等同于 ei_receive
,带有可选的超时参数,请参阅本手册页开头的描述。
ei_reg_send()
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();
ei_reg_send_tmo()
int ei_reg_send_tmo(ei_cnode* ec, int fd, char* server_name, char* buf, int len,
unsigned timeout_ms);
等同于 ei_reg_send
,带有可选的超时参数,请参阅本手册页开头的描述。
ei_rpc()
ei_rpc_to()
ei_xrpc_to()
自 OTP 24.0 起可用
ei_rpc_from()
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_xrpc_to(ei_cnode *ec, int fd, char *mod, char *fun, const char *argbuf,
int argbuflen, int flags);
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 请求并等待结果来结合这两个函数的功能。
当 ei_xrpc_to()
的 flags
参数设置为 0
时,该函数等效于 ei_rpc_to()
。当 ei_xrpc_to()
的 flags 参数设置为 EI_RPC_FETCH_STDOUT
时,将转发 stdout(标准输出)数据。有关 EI_RPC_FETCH_STDOUT
标志的更多信息,请参阅 flags 参数的文档。
另请参阅 Kernel 中的 rpc:call/4
。
ec
是先前通过调用ei_connect_init()
或ei_connect_xinit()
初始化的 C 节点结构。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 元组组成。 2 元组可以采用以下两种形式之一{rex,Reply}
- 此响应值表示 RPC 已完成。结果值是Reply
项。这是可以从通过调用ei_rpc_to()
或没有EI_RPC_FETCH_STDOUT
标志的ei_xrpc_to()
触发的 RPC 获取的唯一响应类型。如果 RPC 是通过调用设置了EI_RPC_FETCH_STDOUT
标志的ei_xrpc_to()
触发的,则已接收所有转发的 stdout 数据。{rex_stdout,StdOutUTF8Binary}
- 只有当 RPC 调用是通过调用ei_xrpc_to()
并设置了EI_RPC_FETCH_STDOUT
标志触发时,才能获取此响应值。此响应值表示已接收到转发的 stdout 数据。stdout 数据存储在二进制文件中,并使用 UTF-8 编码。可能需要多次调用ei_rpc_from()
来读取所有 stdout 数据。stdout 数据的接收顺序与写入顺序相同。当从ei_rpc_from()
调用中获取到{rex,Reply}
元组时,表示已接收到所有转发的 stdout 数据。
flags
标志EI_RPC_FETCH_STDOUT
是目前ei_xrpc_to()
唯一支持的标志。当设置EI_RPC_FETCH_STDOUT
时,被调用的函数会在一个新进程中执行,并使用一个 组长 来转发所有 stdout 数据。这意味着在被调用函数执行期间,由被调用函数及其子进程写入的 stdout 数据都将被转发(前提是组长没有通过调用erlang:group_leader/2
而更改)。转发的 stdout 数据需要通过一系列调用ei_rpc_from()
来收集。有关如何使用ei_rpc_from()
接收 stdout 数据的说明,请参见x
参数的描述。有关组长概念的更多信息,请参见 I/O 协议的文档。注意
只有当与 OTP-24 或更高版本的节点交互时,标志
EI_RPC_FETCH_STDOUT
才有效。
ei_rpc()
成功时返回结果中的字节数,失败时返回 -1
。ei_rpc_from()
返回字节数,否则返回 ERL_TICK
、ERL_TIMEOUT
和 ERL_ERROR
之一。函数 ei_rpc_to()
和 ei_xrpc_to()
成功时返回 0,否则返回 -1。失败时,所有四个函数都会将 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();
ei_self()
erlang_pid * ei_self(ei_cnode *ec);
检索 C 节点的通用 pid。每个 C 节点都有一个(伪)pid,用于 ei_send_reg
、ei_rpc()
等。它包含在 ec
结构的一个字段中。请不要修改此结构。
成功时返回指向进程标识符的指针。失败时返回 NULL
,并设置 erl_errno
。
由 ec
标识的 C 节点必须已初始化,并且在调用 ei_self()
之前必须已收到一个名称。C 节点的初始化是通过调用 ei_connect_init()
或其类似函数完成的。如果名称是从对等节点动态分配的,则 C 节点还必须已连接。
ei_send()
int ei_send(int fd, erlang_pid* to, char* buf, int len);
向进程发送 Erlang 项。
fd
是到 Erlang 连接的打开描述符。to
是消息的预期接收者的 pid。buf
是包含二进制格式的项的缓冲区。len
是消息的长度(以字节为单位)。
如果成功,则返回 0
,否则返回 -1
。在后一种情况下,它将 erl_errno
设置为 EIO
。
ei_send_encoded()
int ei_send_encoded(int fd, erlang_pid* to, char* buf, int len);
与 ei_send
的工作方式完全相同,保留备用名称是为了向后兼容。在没有事先通知的情况下,该函数不会被删除。
ei_send_encoded_tmo()
int ei_send_encoded_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms);
等效于 ei_send_encoded
,带有可选的超时参数,请参见本手册页开头的描述。
ei_send_reg_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 分布协议中)的进程标识符。
可以通过调用 ei_self(cnode_pointer)
从 ei_cnode
结构中检索到合适的 erlang_pid
。
ei_send_reg_encoded_tmo()
int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, const char *to, const char *buf,
int len, unsigned timeout_ms);
等效于 ei_send_reg_encoded
,带有可选的超时参数,请参见本手册页开头的描述。
ei_send_tmo()
int ei_send_tmo(int fd, erlang_pid* to, char* buf, int len, unsigned timeout_ms);
等效于 ei_send
,带有可选的超时参数,请参见本手册页开头的描述。
ei_thisnodename()
ei_thishostname()
ei_thisalivename()
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
结构中提取相应的字段。直接读取该字段在很长一段时间内可能都是安全的,因此实际上不需要这些函数。
ei_unpublish()
int ei_unpublish(ei_cnode *ec);
进程可以调用此函数以从本地主机上的 EPMD 中取消注册指定的节点。但是,通常不允许这样做,除非 EPMD 启动时带有标志 -relaxed_command_check
,而它通常没有此标志。
要取消注册已发布的节点,应关闭 ei_publish()
返回的描述符。
警告
此函数已弃用,将在未来的版本中删除。
ec
是要取消注册的节点的节点结构。
如果节点已成功从 EPMD 中取消注册,该函数将返回 0
。否则,将返回 -1
,并且 erl_errno
将设置为 EIO
。
ei_unpublish_tmo()
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:以上消息和数据转换的进度报告