NODEJS硬实战笔记程序员node.js

NODEJS硬实战笔记 (TCP与UDP)

2017-03-23  本文已影响1978人  77即是正义

NodeJS TCP与UDP

一个最简单的TCP服务端

var net = require('net');
var clients = 0;
    
var server = net.createServer(function (client) {
    
  clients++;
  var clientId = clients;
  console.log('Client connected:', clientId);
    
  client.on('end', function () {
    console.log('Client disconnected:', clientId);
  });
    
  client.write('Welcome client: ' + clientId + 'rn');
  client.pipe(client);
});
    
server.listen(8000, function () {
  console.log('Server started on port 8000');
});

当一个客户端创建了一个新连接,传递给net.createServer回调函数将会执行。回调接收一个面向事件的连接对象。这个服务对象是net.Server的一个实例,仅仅是对net.Socket类的一个封装,而net.Socket类又是使用双工流来实现的,所以服务端在发送信息给客户端的时候可以使用client.pipe()管道来发送。

TCP客户端和服务端

// 主要是验证每次的连接都对应不同的clientId
var assert = require('assert');
var net = require('net');
    
var clients = 0;
var expectedAssertions = 2;
    
// 创建一个服务端
var server = net.createServer(function (client) {
  clients++;
  var clientId = clients;
  console.log('Client connected:', clientId);
    
  client.on('end', function () {
    console.log('Client disconnected:', clientId);
  });
    
  client.write('Welcome client:' + clientId + '\r\n');
  client.pipe(client);
});
    
// 主线程监听8000端口
server.listen(8000, function () {
  console.log('Server started on port 8000');
    
  runTest(1, function () {
    runTest(2, function () {
      console.log('Tests finished');
      assert.equal(0, expectedAssertions);
      server.close()
    })
  });
    
  // 创建客户端并去连接服务端
  function runTest(expectedId, done) {
    var client = net.connect(8000);
    
    client.on('data', function (data) {
      var expected = 'Welcome client:' + expectedId + '\r\n';
      assert.equal(data.toString(), expected);
      expectedAssertions--;
      client.end();
    });
    
    client.on('end', done)
  }
});

这里发现了一个问题,当服务端和客户端处于同一线程中的时候,两边互发消息,服务端接收到的流存在异常,内容将变成客户端发送的消息+服务端之前发送的消息。暂时只知道在write后再通过管道传递会发送这种情况,不知道为什么?并且也不是很清楚为什么在write了之后还需要通过管道传递。

TCP基础知识

数据包与MTU

分片

缓冲区

Nagle算法

UDP服务端和客户端

var assert = require('assert');
var dgram = require('dgram');
var fs = require('fs');
var defaultSize = 16;
var port = 41234;

// 创建客户端
function Client(remoteIP) {
  var socket = dgram.createSocket('udp4');
  var readline = require('readline');
  var rl = readline.createInterface(process.stdin, process.stdout);

  socket.send(new Buffer('<JOIN>'), 0, 6, port, remoteIP);

  rl.setPrompt('Message> ');
  // 开始等待用户的输入
  rl.prompt();

  // 当用户输入完一行按回车后触发
  rl.on('line', function (line) {
    sendData(line)
  // readline一开始执行就不会结束,所以需要监听close事件来关闭进程
  }).on('close', function () {
    process.exit(0)
  });

  socket.on('message', function (msg, rinfo) {
    console.log('\n<' + rinfo.address + '>', msg.toString());
    rl.prompt();
  });

  function sendData(message) {
    socket.send(new Buffer(message), 0, message.length, port, remoteIP,
      function (err, bytes) {
        console.log('Sent:', message);
        rl.prompt();
      })
  }
}

// 创建服务端
function Server() {
  var clients = [];
  var server = dgram.createSocket('udp4');
  server.on('message', function (msg, rinfo) {
    var clientId = rinfo.address + ':' + rinfo.port;
    msg = msg.toString();

    if (!clients[clientId]) {
      clients[clientId] = rinfo;
    }

    if (msg.match(/^</)) {
      console.log('Control message:', msg);
      return;
    }

    for (var client in clients) {
      if (client !== clientId) {
        client = clients[client];
        server.send(
          new Buffer(msg), 0,
          msg.length, client.port, client.address,
          function (err, bytes) {
            if (err) console.error(err);
            console.log('Bytes sent:', bytes);
          }
        )
      }
    }
  });

  server.on('listening', function () {
    console.log('Server ready:', server.address());
  });

  server.bind(port);
}

module.exports = {
  Client: Client,
  Server: Server
};

// module.parent 返回引用该模板的模板
if (!module.parent) {
  switch (process.argv[2]) {
    case 'client':
      new Client(process.argv[3]);
      break;
    case 'server':
      new Server();
      break;
    default:
      console.log('Unknown option');
  }
}

使用dgram.createSocket创建一个客户端socket与服务端相同。发送一个数据报需要一个buffer来承载,用偏移量来表明buffer中消息的开始、消息的长度、服务端口、远程IP和一个可选的回调,当消息发出时会被触发。

部分内容摘抄自网上博客

上一篇下一篇

猜你喜欢

热点阅读