如何用net 创建多路复用的RPC通道

863 阅读4分钟

如何用net 创建多路复用的RPC通道

先导语

我们前面学习了什么是RPC通道,今天就来尝试使用node的net模块来实现一下RPC通道。

关于net

net模块和http模块的用法类似,详情请看:nodejs.org/dist/latest…

使用net创建一个单工通信的RPC通道

  1. 首先先创建两个js文件,分别是:client和server
  2. 然后在server文件里面引入net模块,并使用net模块创建一个服务器,然后监听4000端口(端口号可以自定,我这里监听的是4000端口),代码如下:
    const net = require("net"); //引入net模块

    // 创建server
    // socket:网络通道中写入和取出的一个代理对象
    let server = net.createServer((socket) => {
      socket.on("data", (buffer) => {
        // 监听data事件
        console.log(buffer, buffer.toString());
      });
    });

    server.listen(4000);
  1. 在client文件里面同样引入net模块,创建一个Socket对象,该Socket对象连接server服务器,然后就可以使用socket.write的方法去写入需要发送的内容了。代码如下:
    const net = require("net");

    let socket = new net.Socket({}); //创建socket对象

    //连接server服务器
    socket.connect({
      host: "127.0.0.1",
      port: 4000,
    });

    socket.write("hello wlf"); //发送
  1. 分别启动client和server服务器,server端就可以得到client端发送过来的内容了。

image.png

使用net创建一个半双工通信的RPC通道

半双工通信的特点是:只有当上一次的请求返回结果之后,才能发送下一次请求! client端代码如下:

    const net = require("net");

    let socket = new net.Socket({}); //创建socket对象

    //连接server服务器
    socket.connect({
      host: "127.0.0.1",
      port: 4000,
    });

    socket.write(send(Math.floor(Math.random() * 8))); //发送

    socket.on("data", (buffer) => {

      console.log(buffer.toString());

      //每次得到一个结果之后,设置一个定时器,规定两秒后继续发送消息
      setTimeout(() => {
        socket.write(send(Math.floor(Math.random() * 8))); //发送
      }, 2000);
    });

    // 将发送消息的代码封装成一个方法
    function send(id) {
      let buf = Buffer.alloc(2);
      buf.writeInt16BE(id, 0);
      return buf;
    }

server端代码:

const net = require("net"); //引入net模块

// 创建server
// socket:网络通道中写入和取出的一个代理对象
let server = net.createServer((socket) => {
  socket.on("data", (buffer) => {
    // 监听data事件
    let dataId = buffer.readInt16BE();
    console.log(dataId);
    socket.write(Buffer.from(data[dataId]));
  });
});

server.listen(4000);

//数据
let data = {
  0: "柳宗元",
  1: "韩愈",
  2: "欧阳修",
  3: "苏洵",
  4: "苏轼",
  5: "苏辙",
  6: "王安石",
  7: "曾巩",
};

结果:每个两秒之后发起一次请求并接收到一个结果。

image.png

双工通信

双工通信的特点是:自由的发送和返回消息,根据双工通信的这个特点,我们的代码可能会遇到一个问题:如何确定发送的消息和返回的消息之间的对应关系? 我们画图来解释下这个问题:

image.png 如上图:当client端几乎同时发送了两条消息,分别是msg1和msg2,这个时候server端接收到两条消息,然后开始处理,首先返回出去的是msg2的处理结果,然后才是msg1的处理结果,这个时候client端如何将得到的数据跟发送的数据对应起来呢?

要解决这个问题,我们需要在发送和返回的数据中加上相同的一个标识id。

client端代码:

    const net = require("net");

    let socket = new net.Socket({}); //创建socket对象

    //连接server服务器
    socket.connect({
      host: "127.0.0.1",
      port: 4000,
    });

    // 每50ms发一个包
    setInterval(() => {
      socket.write(send(Math.floor(Math.random() * 8))); //发送
    }, 50);

    socket.on("data", (buffer) => {
      console.log(buffer.slice(0, 2).readInt16BE(0), buffer.slice(2).toString());
    });

    // 将发送消息的代码封装成一个方法
    let flagId = 0; //标识ID
    function send(id) {
      let buf = Buffer.alloc(4);
      buf.writeInt16BE(flagId, 0); //把标识ID放在buffer头部
      buf.writeInt16BE(id, 2);
      flagId++;
      return buf;
    }

server端代码:

const net = require("net"); //引入net模块

// 创建server
// socket:网络通道中写入和取出的一个代理对象
let server = net.createServer((socket) => {
  socket.on("data", (buffer) => {
    // 监听data事件
    let flagBuf = buffer.slice(0, 2); //取出标识ID
    let dataId = buffer.readInt16BE(2);//读出id
    console.log(flagBuf.readInt16BE(0), dataId);
		// 定时器乱序返回结果
    setTimeout(() => {
      socket.write(Buffer.concat([flagBuf, Buffer.from(data[dataId])])); //使用concat连接要返回的内容和标识ID
    }, 1000 * Math.random() + 10);
  });
});

server.listen(4000);

//数据
let data = {
  0: "柳宗元",
  1: "韩愈",
  2: "欧阳修",
  3: "苏洵",
  4: "苏轼",
  5: "苏辙",
  6: "王安石",
  7: "曾巩",
};

client端结果:

image.png

server端结果:

image.png

对比client端和server端可以判单返回数据是否正确。

结束语

今天我们学习了如何使用node的net模块来创建简单的多路复用的RPC通道。通过学习这一节的内容,让我对RPC通道的理解更深刻了。那么,下次见!好好学习,天天向上!

image.png