node.js中net模塊為我們提供了TCP服務器和客戶端通信的各種接口。
一、創建服務器並監聽端口
const net = require('net'); //創建一個tcp服務 //參數一表示創建服務的一些配置 //參數二表示 事件 'connection' 監聽回調函數 let server = net.createServer({ //表示是否允許一個半開的TCP連接,默認為false allowHalfOpen: false, //一旦來了連接,是否暫停套接字,默認為false pauseOnConnect: false }); server.listen(6666); //一個新的連接建立時觸發 'connection' 事件 server.on('connection', function (socket) { //注意這里的socket是一個流,既可以讀,也可以寫 //當我們監聽 'data' 事件后,系統就會不斷的從流中讀取數據 socket.on('data', function (data) { console.log('服務器接收到 : ', data.toString()); }); }); //服務調用 server.listen() 監聽后就會觸發該事件 server.on('listening', function () { // address() 方法返回服務器地址信息對象 let addr = server.address(); console.log(`服務器監聽 : ${addr.port} 端口`); }); //服務關閉時觸發,如果還有連接存在,則直到所有連接結束才會觸發該事件 server.on('close', function () { console.log('服務關閉'); }); //出現錯誤時觸發 server.on('error', function (err) { console.log(err); });
windows下可以通過telnet 或 xshell,putty等工具連接上該服務,進行交互。
我們可以通過 getConnections() 實時獲取當前服務器的連接數。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); //一個新的連接建立時觸發 'connection' 事件 server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); socket.on('data', function (data) { console.log('服務器接收到 : ', data.toString()); }); });
我們可以手動的設置服務器的最大連接數,如果超過該連接數,則會拒絕連接。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); //設置最大連接數為3,當有第4個連接請求時,連接會拒絕。 server.maxConnections = 3; }); //一個新的連接建立時觸發 'connection' 事件 server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); socket.on('data', function (data) { console.log('服務器接收到 : ', data.toString()); }); });
我們也可以使用 close() 手動的拒絕所有連接請求,當已連接的客戶端都關閉后,則服務器會自動關閉,並觸發 'close' 事件。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); //一個新的連接建立時觸發 'connection' 事件 server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); socket.on('data', function (data) { console.log('服務器接收到 : ', data.toString()); }); }); server.on('close', function () { console.log('服務器被關閉'); }); //5秒后手動關閉服務器,拒絕所有連接請求,已有連接全部關閉后,觸發 'close' 事件 setTimeout(function () { server.close(); }, 5000);
調用 unref() 后,則當所有客戶端連接關閉后,將關閉服務器。ref() 功能與 unref() 相反。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); //一個新的連接建立時觸發 'connection' 事件 server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); socket.on('data', function (data) { console.log('服務器接收到 : ', data.toString()); }); socket.on('close', function () { console.log('客戶端關閉'); //調用unref()后,當所有客戶端連接都關閉后,將關閉服務器 server.unref(); }); }); server.on('close', function () { console.log('服務器關閉'); });
二、net.Socket是一個socket端口對象,是一個全雙工的可讀可寫流
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); console.log('客戶端信息 : ', socket.address()); //接收到數據時觸發 socket.on('data', function (data) { //我們可以從流中讀取數據 console.log('服務器接收到 : ', data.toString()); console.log('累計接收的數據大小 : ', socket.bytesRead); console.log('累計發送的數據大小 : ', socket.bytesWritten); //也可以向流中寫入數據 let flag = socket.write(`服務器向你發送 : ${data.toString()} \r\n`); console.log('flag : ', flag); console.log('當前已緩沖並等待寫入流中的字節數 : ', socket.bufferSize); }); //連接關閉時觸發 socket.on('end', function () { console.log('客戶端關閉'); }); //連接完全關閉時觸發 socket.on('close', function () { console.log('客戶端完全關閉'); }); //發生錯誤時觸發 socket.on('error', function (err) { console.log(err); }); });
我們可以通過 puase() 和 resume() 方法暫停讀寫數據。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); //接收到數據時觸發 socket.on('data', function (data) { console.log('接收到的數據 : ', data.toString()); setTimeout(function () { //3秒后暫停讀取數據 socket.pause(); }, 3000); setTimeout(function () { //6秒后恢復讀取數據 socket.resume(); }, 6000); }); });
net.Socket對象是一個流,既然是流,那么我們可以通過 pipe() 方法建一個到文件的可寫流,把從客戶端接收的數據寫入到一個文件中。
const net = require('net'); const fs = require('fs'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); let ws = fs.createWriteStream('./1.txt'); server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); //接收到數據時觸發 socket.on('data', function (data) { console.log('接收到的數據 : ', data.toString()); }); //注意這里的第二個參數,設為 false。 //每當有一個新的連接時,就會創建一個新的 socket 對象。 //不然當第一個socket對象結束操作或關閉時,會自動關閉 ws 可寫流。 //后續的socket對象將無法往 ws 里寫入數據。 socket.pipe(ws, {end: false}); socket.on('end', function () { console.log('客戶端關閉'); socket.unpipe(ws); }); });
通過 setTimeout() 我們可以設置一個連接活動的超時時間,當一個連接超時時,將觸發 'timeout' 事件。
const net = require('net'); let server = net.createServer(); server.listen(6666, '0.0.0.0', function () { console.log('服務器監聽開始'); }); server.on('connection', function (socket) { //獲取當前服務器的連接數 server.getConnections(function (error, count) { console.log('當前服務器的連接數 : ', count); }); //接收到數據時觸發 socket.on('data', function (data) { console.log('接收到的數據 : ', data.toString()); }); socket.setTimeout(3 * 1000); //連接超時后,並不會斷開,需手動調用 end() 或 destroy() 來斷開連接 socket.on('timeout', function () { console.log('當前連接已超時'); }); });
三、創建一個tcp客戶端
const net = require('net'); //創建一個tcp客戶端 let client = new net.Socket(); client.connect({ host: '127.0.0.1', port: 6666 }); //客戶端與服務器建立連接觸發 client.on('connect', function () { client.write('你好服務器'); }); //客戶端接收數據觸發 client.on('data', function (data) { console.log('服務器發送的數據 : ', data.toString()); });
客戶端可以通過調用 end() 方法來主動關閉與服務端的連接。
const net = require('net'); //創建一個tcp客戶端 let client = new net.Socket(); client.connect({ host: '127.0.0.1', port: 6666 }); //客戶端與服務器建立連接時觸發 client.on('connect', function () { //往服務端寫入數據 client.write('你好服務器'); }); //客戶端接收數據時觸發 client.on('data', function (data) { console.log('服務器發送的數據 : ', data.toString()); }); setTimeout(function () { client.end(); }, 3000);
通過 setKeepAlive() 方法來禁用或啟用長連接功能,防止時間過短而斷開連接。setKeepAlive() 會發送一個空包,來保持通信。
const net = require('net'); //創建一個tcp客戶端 let client = new net.Socket(); client.connect({ host: '127.0.0.1', port: 6666 }); //設置連接保持 client.setKeepAlive(true, 3000); //客戶端與服務器建立連接觸發 client.on('connect', function () { client.write('你好服務器'); }); //客戶端接收數據觸發 client.on('data', function (data) { console.log('服務器發送的數據 : ', data.toString()); }); setTimeout(function () { client.end(); }, 3000);