node之tcp篇入門理解

蘆夢宇發表於2018-06-09

在Node.js中,net模組實現了基於TCP的資料通訊

// node實現tcp協議 給我們提供了一個包 net模組
let net = require('net');
// socket套接字 會話,http的請求和響應就是基於socket拆分出來的
let server = net.createServer();
// 當客戶端請求時,會觸發 connection
server.on('connection',function (socket) {
 console.log('客戶端請求到來了')
});
let port = 3000;
server.listen(port,function () {
  console.log(`server start ${port}`);
});
複製程式碼

伺服器監聽3000埠事件,開啟服務,此時命令列輸入 telnet localhost 3000,觸發'connection'事件,列印出'客戶端請求到來了'

socket是一個Duplex 雙工流,伺服器既可以可以給客戶端寫東西,也可以收到客戶端的東西;同樣客戶端既可以收到服務端發的東西,也可以給伺服器發東西。 如下:

server.on('connection',function (socket) {
     console.log('客戶端請求到來了')
    //socket是一個Duplex雙工流, 可讀可寫
      socket.write('歡迎光臨');//伺服器給客戶端寫了‘歡迎光臨’
      //socket.end(); // 伺服器關掉客戶端
      socket.setEncoding('utf8');// 設定編碼格式
      // 可以通過流的方式接收到客戶端發來的訊息
      socket.on('data',function (data) {
         console.log(data);
      });
  });
複製程式碼

socket.end()是伺服器關掉客戶端,那麼伺服器可以不可以關閉自己呢?答案是可以的。有兩種方式: server.close() vs server.unref() server.close():如果觸發close事件就不會再接收新的請求了。當處理完手頭裡的請求後,會觸發close事件並把自己關閉。 server.unref(): 如果觸發unref事件依然會接收新請求,當處理所有請求後,會把自己關閉,不會觸發close事件。

// 寫一個聊天室 
server.on('connection',function (socket) {
  //socket是一個Duplex 可讀可寫
  socket.write('歡迎光臨');
  //socket.end(); // 相當於關掉客戶端
  socket.setEncoding('utf8');
  // 可以通過流的方式接收到資料
  socket.on('data',function (data) {
     console.log(data);
     server.close(); // 如果觸發close事件就不會再接收新的請求了
     //server.unref(); // 也表示關閉 ,沒有客戶端連線會自己關閉(不會觸發close事件)
     // unref可以接收新的請求,close不能接收新的請求了
  });
});
server.on('close',function () {
  console.log('伺服器關閉');
});

複製程式碼

另外我們可以設定最大客戶端連線數目:

//設定最大連結數以及監聽客戶端的連結數量
server.getConnections((err,count)=>{
    console.log('已經連結'+count+'個使用者')
});
server.maxConnections = 2; // 設定最大連線數,超過數量不能連線
複製程式碼

接下來我們基於tcp寫一個簡單的聊天室吧 自定義下規則: // l 表示看所有的線上人數 // s:zs:哈哈 表示:私信給zs,內容是哈哈 // r:zs 將自己重新命名為zs // b 表示廣播和所有人說話

let net = require('net');
let client = {};
let server = net.createServer(function (socket) {
  server.maxConnections = 4; //最大連線數
  server.getConnections(function (err, count) {
    socket.write(`歡迎到來當前人數為${count},總容納數${server.maxConnections}\r\n`);
  });
  let key = socket.remoteAddress + socket.remotePort;//每個使用者的唯一標示
  socket.setEncoding('utf8');
  client[key] = { nickName: '匿名', socket };
  socket.on('data', function (chunk) {
    chunk = chunk.replace(/\r\n/g, '');
    let char = chunk.split(':')[0];
    let content = chunk.split(':')[1];
    switch (char) {
      case 'l':
        showList(socket); // 顯示使用者列表
        break;
      case 's': // s:zs:內容
        private(chunk.split(':')[1], chunk.split(':')[2], client[key].nickName);
        break;
      case 'r':
        rename(key, content); // r:xxx
        break;
      case 'b':
        broadcast(chunk.split(':')[1], client[key].nickName);
      default:
        break;
    }
  })
});
複製程式碼

顯示列表:

function showList(socket) {
  let users = [];
  Object.keys(client).forEach(key => {
    users.push(client[key].nickName);
  });
  socket.write(`當前使用者列表:\r\n${users.join('\r\n')}\r\n`);
}
複製程式碼

重新命名:

function rename(key, chunk) {
  client[key].nickName = chunk;
}
複製程式碼

私信:

/**
nickName:被私信的人
content:私信的內容
n:主動私信的人
**/
function private(nickName, content, n) {
  // {key:{nickName,socket}}
  let s;
  Object.keys(client).forEach(key => {
    if (client[key].nickName === nickName) {
      s = client[key].socket
    }
  });
  s.write(`${n}:${content}\r\n`);
}
複製程式碼

廣播:

/**
chunk:內容
nickName:廣播人的名字
**/
function broadcast(chunk, nickName) {
  Object.keys(client).forEach((nick) => {
    if (client[nick].nickName !== nickName) {
      client[nick].socket.write(`${nickName}:${chunk}\r\n`);
    }
  });
}
複製程式碼

tcp不僅可以建立服務端還可以建立客戶端

let net = require('net');
// 建立tcp客戶端
let socket = net.createConnection({port:3000},function () {
  socket.write('hello');
  socket.on('data',function (data) {
    console.log(data);
  })
});
複製程式碼

相關文章