通过EPMD来获取Erlang Node的Port

论坛 期权论坛 脚本     
匿名技术用户   2021-1-4 19:39   49   0

在Erlang分布式中,各个节点之间的通讯都是通过Erlang的EPMD (Erlang Port Mapper Daemon)来实现的。首先,节点在EPMD注册节点名称,然后客户端或者另外一个节点与注册的节点通讯时,发送请求数据到EPMD,然后EPMD根据请求内容返回相应的相应信息,客户端或者另外一个节点再根据返回信息与服务节点通讯,详细的Erlang分布式协议可以通过http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html#id2285540得到。

在这里,主要是简单的介绍一下,客户端与服务节点通讯前是如何发送请求信息到EPMD,并获取服务节点的Port后,再与服务节点实现TCP通讯的。

客户端是通过端口号4369来实现与EPMD通讯的。在发送请求数据时,每个请求中的前两个字节是代表本次请求的内容长度(如a图)。在请求获取服务节点端口号中,请求内容则是占有一个字节的请求标识符和服务节点名称(b图)。

a、请求数据格式

2 n
Length Request

b、请求内容格式

1 N
122 NodeName

当EPMD接受请求后,返回响应信息,响应信息的第一个字节是一个标识符119,第二个字节是返回结果,如果结果大于0,则说明未找到对应服务节点;结果等于0,则后面接着的是对应的服务节点信息。

a、返回结果大于0,返回的数据格式

1 1
119 Result

b、返回结果等于0,返回的数据格式

1 1 2 1 1 2 2 2 Nlen 2 Elen
119 Result PortNo NodeType Protocol HighestVersion LowestVersion Nlen NodeName Elen Extra

上面的所有第一行中的数字都是表示的字节数。

通过上面的简单介绍,已经可以初步了解其实现原理了,为了更好的理解,下面是用C#来模拟实现。

class EPMD

{

private string _alive;

private string _host;

private string _node;

public EPMD(System.String nodeName)

{

int i = nodeName.IndexOf((System.Char)'@', 0);

if (i < 0)

{

_alive = nodeName;

_host = null;

}

else

{

_alive = nodeName.Substring(0, (i) - (0));

_host = nodeName.Substring(i + 1, (nodeName.Length) - (i + 1));

}

if (_alive.Length > 0xff)

{

_alive = _alive.Substring(0, (0xff) - (0));

}

_node = _alive + "@" + _host;

}

public int GetPort()

{

TcpClient client = new TcpClient(_host, 4369);

NetworkStream stream = client.GetStream();

byte[] buf = new Byte[3 + _alive.Length];

// 前两个字节写入本次请求数据长度

buf[0] = 0;

buf[1] = (byte)(_alive.Length + 1);

//第三个字节写入本次请求标识符

buf[2] = 122;

// 写入请求的节点名称

byte[] data = System.Text.Encoding.UTF8.GetBytes(_alive);

for (int i = 0; i < data.Length; i++)

{

buf[i + 3] = data[i];

}

// 向EPMD发送请求数据

stream.Write(buf, 0, buf.Length);

byte[] tmpbuf = new byte[100];

// 获取响应数据

int n = stream.Read(tmpbuf, 0, 100);

// n小于0,则请求失败

if (n < 0)

{

client.Close();

Console.WriteLine("在主机上{0}未获取到EPMD相应", _host);

return 0;

}

MemoryStream mem = new MemoryStream(tmpbuf);

// 第一位,获取响应标识符

int port_resp = mem.ReadByte();

// 第二位,获取响应结果

int result = mem.ReadByte();

// 返回与节点对应的端口信息

if (result == 0)

{

// 读取2个字节数据,获取端口号

byte[] b = new byte[2];

mem.Read(b, 0, b.Length);

return ((((int)b[0] << 8) & 0xff00) + (((int)b[1]) & 0xff));

}

return 0;

}

}

static void Main(string[] args)

{

EPMD test = new EPMD("servernode@liyiqun");

int port = test.GetPort();

Console.ReadKey(true);

}

如果不确定自己获取的端口号是否正确,还可以在erlang shell中通过执行net_adm:names().函数来验证是否一致。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP