通过EPMD来获取Erlang Node的Port

转载:http://blog.csdn.net/minyskirt/article/details/5319419


在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().函数来验证是否一致。


你可能感兴趣的:(String,Stream,erlang,shell,byte,通讯)