Nodejs 一个tcp例子,解包

Nodejs 一个tcp例子,解包

写了个tcp例子,抛砖引玉

client, 发送一个字符串

var net = require('net');

var socket = net.connect(5002, ‘localhost’, function() { var str=‘abc’; var len = Buffer.byteLength(str); var buf = new Buffer(len + 2); buf.writeUInt16BE(len, 0); buf.write(str, 2); for(var i=0; i<100; i++) { socket.write(buf); } })

server 端,用on message来处理信息。

var net = require('net');

function getDataParser(emmiter) {

  var waiting = 0
    , buf
    , bufOffset
    ;

  function parseData(data, offset) {
    var len = data.length;
    var left = len - offset;
      if(waiting) {
        var toRead = Math.min(left, waiting);
        if(waiting <= left) {
          data.copy(buf, bufOffset, offset, offset + waiting);
          offset += waiting;
          waiting = 0;
          emmiter.emit('message', buf);
          if (offset < len) {
            parseData(data, offset);
          }
        } else {
          data.copy(buf, bufOffset, offset, len);
          waiting -= (len - offset);
        }
      } else {
        waiting = data.readUInt16BE(offset);
        buf = new Buffer(waiting);
        // UINT length is 2
        parseData(data, offset + 2)
      }
  }

  return parseData;
}

function handler(socket) {
  var parseData = getDataParser(socket);

  socket.on('data', function(data) {
      console.log('data');
      console.log(data);
      parseData(data, 0);
  });

  socket.on('message', function(buf) {
      console.log('msg');
      console.log(buf);
  })
}

net.createServer(handler).listen(5002);

6 回复

Node.js TCP 示例:解包

本文将介绍如何使用 Node.js 实现一个简单的 TCP 客户端和服务端通信,并且在服务端实现数据包的解析。

客户端代码

客户端的任务是连接到服务器并发送一个字符串。我们使用 net 模块创建一个 TCP 连接,并通过这个连接发送数据。

var net = require('net');

var socket = net.connect(5002, 'localhost', function() {
    var str = 'abc';
    var len = Buffer.byteLength(str);
    var buf = new Buffer(len + 2);
    buf.writeUInt16BE(len, 0);  // 写入长度前缀
    buf.write(str, 2);         // 写入实际数据
    for (var i = 0; i < 100; i++) {
        socket.write(buf);     // 发送数据包
    }
});

在这个例子中,我们首先计算字符串的长度(Buffer.byteLength(str)),然后创建一个包含长度前缀和实际数据的缓冲区。长度前缀是一个 2 字节的无符号整数,表示后面跟随的实际数据的长度。然后我们把这个缓冲区发送给服务器 100 次。

服务端代码

服务端的任务是接收客户端发送的数据,并正确地解析这些数据包。为了实现这一点,我们需要处理粘包问题(即多个数据包可能被合并成一个数据包发送)。

var net = require('net');

function getDataParser(emitter) {
    var waiting = 0;
    var buf;
    var bufOffset;

    function parseData(data, offset) {
        var len = data.length;
        var left = len - offset;

        if (waiting) {
            var toRead = Math.min(left, waiting);
            if (waiting <= left) {
                data.copy(buf, bufOffset, offset, offset + waiting);
                offset += waiting;
                waiting = 0;
                emitter.emit('message', buf);
                if (offset < len) {
                    parseData(data, offset);
                }
            } else {
                data.copy(buf, bufOffset, offset, len);
                waiting -= (len - offset);
            }
        } else {
            waiting = data.readUInt16BE(offset);
            buf = new Buffer(waiting);
            parseData(data, offset + 2);
        }
    }

    return parseData;
}

function handler(socket) {
    var parseData = getDataParser(socket);

    socket.on('data', function(data) {
        parseData(data, 0);
    });

    socket.on('message', function(buf) {
        console.log('Received message:', buf.toString());
    });
}

net.createServer(handler).listen(5002);

在服务端代码中,我们定义了一个 getDataParser 函数,该函数用于解析从客户端收到的数据。解析逻辑包括检查当前缓冲区是否已经包含了完整的消息,如果还没有,则继续读取更多的数据,直到完整的消息被读取为止。一旦完整的消息被读取,它就会被传递给 'message' 事件处理器。

最后,我们在 net.createServer 中注册了这个处理函数,并启动了服务器监听 5002 端口。

这样我们就完成了一个简单的 TCP 客户端和服务端通信的例子,其中服务端能够正确地解析客户端发送的数据包。


沒人感興趣啊

這個例子是將包長度放在包頭,保證可以整條整條消息的讀取。 反饋機制是可以的,序列號挺好的,也不會大很多,序列號16位就夠了,循環使用。

如果发过来的包不完整,好像没有校验

对包的整体作一个加法校验放到包头里 当然也可以做crc检验

在这个帖子中,您提供了一个简单的TCP客户端和服务器示例,并且重点在于如何解包接收到的数据。以下是对您的问题的回答,包含示例代码和简要解释。

客户端

客户端发送一个字符串,并在每次发送时添加一个长度前缀(2字节),这样服务器可以知道每个消息的长度。这有助于正确地解包数据。

var net = require('net');

var socket = net.connect(5002, 'localhost', function() {
    var str = 'abc';
    var len = Buffer.byteLength(str);
    var buf = new Buffer(len + 2);
    buf.writeUInt16BE(len, 0);  // 写入长度前缀
    buf.write(str, 2);  // 写入实际数据
    socket.write(buf);  // 发送数据
});

socket.on('end', function() {
    console.log('Connection closed');
});

服务器端

服务器端使用自定义的getDataParser函数来解析数据。该函数首先读取长度前缀,然后根据该长度读取实际的数据。

var net = require('net');

function getDataParser(emitter) {
  let waiting = 0;
  let buf;
  let bufOffset;

  function parseData(data, offset) {
    const len = data.length;
    const left = len - offset;
    
    if (waiting) {
      const toRead = Math.min(left, waiting);
      if (waiting <= left) {
        data.copy(buf, bufOffset, offset, offset + waiting);
        offset += waiting;
        waiting = 0;
        emitter.emit('message', buf);
        if (offset < len) {
          parseData(data, offset);
        }
      } else {
        data.copy(buf, bufOffset, offset, len);
        waiting -= (len - offset);
      }
    } else {
      waiting = data.readUInt16BE(offset);
      buf = new Buffer(waiting);
      parseData(data, offset + 2);
    }
  }

  return parseData;
}

function handler(socket) {
  const parseData = getDataParser(socket);

  socket.on('data', function(data) {
    parseData(data, 0);
  });

  socket.on('message', function(buf) {
    console.log('Message received:', buf.toString());
  });
}

net.createServer(handler).listen(5002);

解释

  • 客户端:向服务器发送一个带有长度前缀的字符串。
  • 服务器端:通过getDataParser函数解析数据,首先读取2字节的长度前缀,然后根据长度前缀读取实际数据,并将其传递给'message'事件处理器。

这样,即使发送的数据是分块到达的,服务器也能正确地解包并处理这些数据。

回到顶部