Node 深刻TCP模塊

1. TCP

在Node.js中,提供了net模塊用來實現TCP服務器和客戶端的通訊。緩存

1.1 TCP服務器

net.createServer([options][, connectionListener])
複製代碼
  • options.allowHalfOpen 是否容許單方面鏈接,默認值爲false
  • connectionListener參數用於指定當客戶端與服務器創建鏈接時所要調用的回調函數,回調中有一個參數socket,指的是TCP服務器監聽的socket端口對象

也能夠經過監聽connection事件的方式來指定監聽函數bash

server.on('connection',function(socket){});
複製代碼

1.1.1 啓動TCP服務器

能夠使用listen方法通知服務器開始監聽客戶端的鏈接服務器

server.listen(port,[host],[backlog],[callback])
複製代碼
  • port 必須指定的端口號
  • host 指定須要監聽的IP地址或主機名,若是省略的話服務器將監聽來自於任何客戶端的鏈接
  • backlog指定位於等待隊列中的客戶端鏈接的最大數量,默認值爲511
server.on('listening',function(){});
複製代碼

1.1.2 使用TCP服務器

let net = require('net');
let server = net.createServer(function(socket){
  console.log('客戶端已鏈接');
});
server.listen(8080,'localhost',function(){
    console.log('服務器開始監聽');
});
複製代碼

1.1.3 address

server.address()
複製代碼
  • port 端口號
  • address TCP服務器監聽的地址
  • family 協議的版本

1.1.4 getConnections

查看當前與TCP服務器創建鏈接的客戶端的鏈接數量以及設置最大鏈接數量網絡

server.getConnections(callback);
 server.maxConnections = 2;
複製代碼

1.1.5 close

使用close方法能夠顯式拒絕全部的客戶端的鏈接請求,當全部已鏈接的客戶端關閉後服務器會自動關閉,並觸發服務器的close事件。socket

server.close();
server.on('close',callback);
複製代碼

1.2 socket

1.2.1 address

net.Socket表明一個socket端口對象,它是一個可讀可寫流。函數

let net = require('net');
let util = require('util');
let server = net.createServer(function(socket){
  server.getConnections((err,count)=>{
      server.maxConnections = 1;
      console.log('最大鏈接數量%d,當前鏈接數量%d',server.maxConnections,count); 
  });  
  let address = socket.address();
  console.log('客戶端地址 %s',util.inspect(address));
});
複製代碼

1.2.2 讀取數據

let server = net.createServer(function (socket) {
    socket.setEncoding('utf8');
    socket.on('data', function (data) {
        console.log('本次收到的內容爲%s,累計收到的字節數是%d', data, socket.bytesRead);
    });
});
複製代碼

1.2.3 監聽關閉事件

let server = net.createServer(function (socket) {
    socket.on('end', function () {
        console.log('客戶端已經關閉');
    });
});
複製代碼

1.2.4 pipe

pipe方法能夠將客戶端發送的數據寫到文件或其它目標中。oop

socket.pipe(destinatin,[options]);
複製代碼
  • options.end 設置爲false時當客戶端結束寫操做或關閉後並不會關閉目標對象,還能夠繼續寫入數據
let net = require('net');
let path = require('path');
let ws = require('fs').createWriteStream(path.resolve(__dirname, 'msg.txt'));
let server = net.createServer(function (socket) {
    socket.on('data', function (data) {
        console.log(data);
    });
    socket.pipe(ws, { end: false });
    socket.on('end', function () {
        ws.end('over', function () {
            socket.unpipe(ws);
        });
    });
});
複製代碼

1.2.5 unpipe

const net = require('net');
const path = require('path');
let file = require('fs').createWriteStream(path.join(__dirname, 'msg.txt'));
let server = net.createServer(function (socket) {
    socket.pipe(file, {
        end: false
    });
    setTimeout(function () {
        file.end('bye bye');
        socket.unpipe(file);
    }, 5000);
    // socket.on('end', function () {
    //     file.end('bye bye');
    // });
});
server.listen(8080);
複製代碼

1.2.5 pause&resume

pause能夠暫停data事件觸發,服務器會把客戶端發送的數據暫存在緩存區裏ui

const net = require('net');
const net = require('net');
const path = require('path');
let file = require('fs').createWriteStream(path.join(__dirname, 'msg.txt'));
let server = net.createServer(function (socket) {
    socket.pause();
    setTimeout(function () {
        socket.resume();
        socket.pipe(file);
    }, 10 * 1000);
});
server.listen(8080);
複製代碼

1.2.6 setTimeout

let net = require('net');
let path = require('path');
let ws = require('fs').createWriteStream(path.resolve(__dirname, 'msg.txt'));
let server = net.createServer(function (socket) {
    socket.setTimeout(5 * 1000);
    socket.pause();
    socket.on('timeout', function () {
        socket.pipe(ws);
    });

    //socket.setTimeout(0);取消超時時間的設置
});
server.listen(8080);
複製代碼

1.2 TCP客戶端

1.2.1 建立TCP客戶端

let socket = new net.Socket([options])
複製代碼
  • fd socket文件描述符
  • type 客戶端全部協議
  • allowHalfOpen 是否容許半鏈接,服務器收到FIN包時不回發FIN包,能夠使服務器能夠繼續向客戶端發數據
socket.connect(port, host, callback);
socket.on('connect', callback);
複製代碼

1.2.2 向服務器端寫入數據、end 、error、destroy,close

  • write表示向服務器寫入數據
  • end 用於結束鏈接
  • error 鏈接發生錯誤
  • destroy 銷燬流
  • close 表示鏈接關閉成功,hasError=true表明有可能有錯誤
    socket.write(data,[encoding],[callback]);
    複製代碼
let net = require('net');
let server = net.createServer(function (socket) {
    console.log("客戶端已經鏈接");
    socket.setEncoding('utf8');
    socket.on('data', function (data) {
        console.log("已接收客戶端發送的數據:%s", data);
        socket.write('服務器:' + data);
    })
    socket.on('error', function (err) {
        console.log('與客戶端通訊過程當中發生了錯誤,錯誤編碼爲%s', err.code);
        socket.destroy();
    });
    socket.on('end', function (err) {
        console.log('客戶端已經關閉鏈接');
        socket.destroy();
    });
    socket.on('close', function (hasError) {
        console.log(hasError ? '異常關閉' : '正常關閉');
    });
});
server.listen(808, function () {
    let client = new net.Socket();
    client.setEncoding('utf8');
    client.connect(808, '127.0.0.1', function () {
        console.log('客戶端已鏈接');
        client.write('hello');
        setTimeout(function () {
            client.end('byebye');
        }, 5000);
    });
    client.on('data', function (data) {
        console.log('已經接收到客戶端發過來的數據:%s', data);
    });
    client.on('error', function (err) {
        console.log('與服務器通訊過程當中發生了錯誤,錯誤編碼爲%s', err.code);
        client.destroy();
    });

});
複製代碼

1.2.3 close

中止server接受創建新的connections並保持已經存在的connections編碼

server.getConnections((err, count) => {
      if (count == 2) server.close();
 });
複製代碼

1.2.4 unref&ref

unref方法指定發客戶端鏈接被所有關閉時退出應用程序 若是將allowHalfOpen方法,必須使用與客戶端鏈接的socket端口對象的end 方法主動關閉服務器端鏈接spa

let net = require('net');
let server = net.createServer({ allowHalfOpen: true }, function (socket) {
    console.log("客戶端已經鏈接");
    socket.setEncoding('utf8');
    socket.on('data', function (data) {
        console.log("已接收客戶端發送的數據:%s", data);
        socket.write('服務器確認數據:' + data);
    })
    socket.on('error', function (err) {
        console.log('與客戶端通訊過程當中發生了錯誤,錯誤編碼爲%s', err.code);
        socket.destroy();
    });
    socket.on('end', function (err) {
        console.log('客戶端已經關閉鏈接');
        socket.end();
        server.unref();
    });
    socket.on('close', function (hasError) {
        if (hasError) {
            console.log('因爲錯誤致使socket關閉');
            server.unref();
        } else {
            console.log('端口正常關閉');
        }
    });
    server.getConnections((err, count) => {
        if (count == 2) server.close();
    });
});
server.listen(808, function () { });
server.on('close', function () {
    console.log('服務器關閉');
});
複製代碼

1.2.5 bufferSize

write的返回值和bufferSize屬性值

let server = net.createServer({ allowHalfOpen: true }, function (socket) {
    console.log("客戶端已經鏈接");
    socket.setEncoding('utf8');
    let rs = fs.createReadStream(path.resolve(__dirname, 'a.txt'), { highWaterMark: 2 });
    rs.on('data', function (data) {
        let flag = socket.write(data);
        console.log("flag:", flag);
        console.log('緩存字節:' + socket.bufferSize);
        console.log('已發送字節:' + socket.bytesWritten);
    })
    socket.on('data', function (data) {
        console.log('data', data);
    });
    socket.on('drain', function (err) {
        "緩存區已所有發送"
    });
});
複製代碼

1.2.6 keepAlive

當服務器和客戶端創建鏈接後,當一方主機忽然斷電、重啓、系統崩潰等意外狀況時,未來不及向另外一方發送FIN包,這樣另外一方將永遠處於鏈接狀態。 能夠使用setKeepAlive方法來解決這一個問題

socket.setKeepAlive([enaable],[initialDelay]);
複製代碼
  • enable 是否啓用嗅探,爲true時會不但向對方發送探測包,沒有響應則認爲對方已經關閉鏈接,本身則關閉鏈接
  • initialDelay 多久發送一次探測包,單位是毫秒

1.2.7 聊天室1.0

/**
 * 1.建立一個服務器
 * 2. 客戶端能夠鏈接服務器
 * 3.客戶端能夠發言,而後廣播給你們
 * 4.客戶端鏈接和退出後都要通知你們。
 * 5.顯示當前的在線人數
 */
let net = require('net');
let util = require('util');
let clients = {};
let server = net.createServer(function (socket) {
    server.getConnections(function (err, count) {
        socket.write(`weclome,there is ${count} users now,please input your username\r\n`);
    });
    let nickname;
    socket.setEncoding('utf8');
    socket.on('data', function (data) {
        data = data.replace(/\r\n/, '');
        if (data == 'byebye') {
            socket.end();
        } else {
            if (nickname) {
                broadcast(nickname, `${nickname}:${data}`);
            } else {
                nickname = data;
                clients[nickname] = socket;
                broadcast(nickname, `welcome ${nickname} joined us!`);
            }
        }

    });
    socket.on('close', function () {
        socket.destroy();
    });
}).listen(8088);

function broadcast(nickname, msg) {
    for (let key in clients) {
        if (key != nickname) {
            clients[key].write(msg + '\r\n');
            clients[nickname].destroy();
            delete clients[nickname];
        }
    }
}
複製代碼

1.2.8 聊天室2.0

var key = scoket.remoteAddress+':'+socket.remotePort;
users[key] = {name:'匿名',socket};

socket.on('data',function(){
    parse(data);
});
function parse(msg){
  swtich(msg.type){
   case 'secret':
     secret(msg.user,msg.text);
     break;
  }
  case 'boardcast':
     boardcast(message.text);
     break;
  case 'cname':
     cname(messsage.text);
     break;
  case 'list':
     list();
     break;   
  default:
     socket.write('不能識別命令');
     break;
}
function secret(user,text){

}
function boardcast(text){

}
function cname(text){

}
function list(){

}
複製代碼
b:text 廣播
c:nickname:text 私聊
n:nickname 更名
l 列出在線用戶列表

on('data',function(data){
   if(data == 'quit){ }else if(data == 'help'){ }else(){ write(data); } }); function convert(){ } 複製代碼

1.3 類方法

  • isIP 判斷字符串是不是IP
  • isIPv4 判斷字符串是不是IPv4地址
  • isIPv6 判斷字符串是不是IPv6地址

2. UDP

2.1 建立socket

let socket = dgram.createSocket(type,[callback]);
socket.on('messsage',function(msg,rinfo){});
複製代碼
  • type 必須輸入,制定時udp4仍是udp6
  • callback 從該接口接收到數據時調用的回調函數
    • msg 接收到的數據
    • rinfo 信息對象
      • address 發送着的地址
      • family ipv4仍是ipv6
      • port 發送者的socket端口號
      • size 發送者所發送的數據字節數
socket.bind(port,[address],[callback]);
socket.on('listening',callabck;
複製代碼
  • port 綁定的端口號
  • address 監聽的地址
  • callback 監聽成功後的回調函數

2.2 向外發送數據

若是發送數據前尚未綁定過地址和端口號,操做系統將爲其分配一個隨機端口並能夠接收任何地址的數據

socket.send(buf,offset,length,port,address,[callback]);
複製代碼
  • buffer 表明緩存區
  • offset 從緩存區第幾個字節開始發
  • length 要發送的字節數
  • port 對方的端口號
  • address 接收數據的socket地址
  • callback 制定當數據發送完畢時所須要的回調函數

    • err 錯誤對象
    • byets 實際發送的字節數

    2.3 address

    獲取此socket相關的地址信息

    let address = socket.address();
    複製代碼
  • port
  • address
  • family

2.5 UDP服務器

var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message',function(msg,rinfo){
  console.log(msg.toString());
  console.log(rinfo);
   socket.send(msg,0,msg.length,rinfo.port,rinfo.address);
});
socket.bind(41234,'localhost');
複製代碼

2.6 UDP客戶端

var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message',function(msg,rinfo){
    console.log(msg.toString());
    console.log(rinfo);
});
socket.setTTL(128);
socket.send(new Buffer('zz'),0,6,41234,'localhost',function(err,bytes){
    console.log('發送了個%d字節',bytes);
});
socket.on('error',function(err){
    console.error(err);
});
複製代碼

2.7 廣播

建立一個UDP服務器並經過該服務器進行數據的廣播

2.7.1 服務器

let dgram = require('dgram');
let server = dgram.createSocket('udp4); server.on('message',function(msg){ let buf = new Bufffer('已經接收客戶端發送的數據'+msg); server.setBroadcast(true); server.send(buf,0,buf.length,41235,"192.168.1.255"); }); server.bind(41234,'192.168.1.100'); 複製代碼

2.7.2 客戶端

let dgram = require('dgram');
let client = dgram.createSocket('udp4); client.bind(41235,'192.168.1.102);
let buf = new Buffer('hello');
client.send(buf,0,buf.length,41234,'192.168.1.100');
client.on('message',function(msg,rinfo){
  console.log('received : ',msg);
});
複製代碼

2.8 組播

  • 所謂的組播,就是將網絡中同一業務類型進行邏輯上的分組,從某個socket端口上發送的數據只能被該組中的其餘主機所接收,不被組外的任何主機接收。
  • 實現組播時,並不直接把數據發送給目標地址,而是將數據發送到組播主機,操做系統將把該數據組播給組內的其餘全部成員。
  • 在網絡中,使用D類地址做爲組播地址。範圍是指 224.0.0.0 ~ 239.255.255.255,分爲三類
    • 局部組播地址: 224.0.0.0 ~ 224.0.0.255 爲路由協議和其餘用途保留
    • 預留組播地址: 224.0.1.0 ~ 238.255.255.255 可用於全球範圍或網絡協議
    • 管理權限組播地址 : 239.0.0.0 ~ 239.255.255.255 組織內部使用,不可用於Internet

把該socket端口對象添加到組播組中。

socket.addMembership(multicastAddress,[multicastInterface]);
複製代碼
  • multicastAddress 必須指定,須要加入的組播組地址
  • multicastInterface 可選參數,須要加入的組播組地址
socket.dropMembership(multicastAddress,[multicastInterface]);
socket.setMulticastTTL(ttl);
socket.setMulticastLoopback(flag);
複製代碼

2.8.1 服務器

let dgram = require('dgram');
let server = dgram.createSocket('udp4');
server.on('listening',function(){
  server.MulticastTTL(128);
  server.setMulticastLoopback(true);
  server.addMembership('230.185.192.108');
});
setInterval(broadcast,1000);
function broadcast(){
  let buffer = Buffer.from(new Date().toLocaleString());
  server.send(buffer,0,buffer.length,8080,"230.185.192.108");
}
複製代碼

2.8.2 客戶端

let dgram = require('dgram');
let client = dgram.createSocket('udp4');
client.on('listening',function(){
    client.addMembership('230.185.192.108');
});
client.on('message',function(message,remote){
  console.log(message.toString());
});
client.bind(8080,'192.168.1.103');複製代碼
相關文章
相關標籤/搜索