Node.Js中TCP粘包、分包解決方案!

Stick

Node.Js中TCP粘包、分包解決方案!

持續更新,源碼地址,喜歡的話請點star,想訂閱點watchgit


目錄

  • 安裝
  • 項目特色
  • 配置介紹
  • API
  • 更新記錄
  • 使用方法
  • 案例演示

安裝

npm i stickpackage

項目特色:

  • 低內存佔用,採用循環隊列數據結構,進行解包處理,隊列內存用戶依據報文大小進行配置。
  • 高併發,目前線上業務每秒1500個數據包,每一個數據包300字節,內存穩定30M之內,單核cpu穩定40%之內

配置介紹

  • [x] 提供對TCP粘包處理的解決方案
  • [x] 默認緩衝512個字節,當接收數據超過512字節,自動以512倍數擴大緩衝空間
  • [x] 本默認採用包頭兩個字節表示包長度
  • [x] 默認採用大端接模式接收數據
  • [x] 能夠配置大端小端讀取
  • [x] 能夠配置自定義包頭長度
  • [x] 支持自動拆解包

API

  • stick(bufferSize) => 直接處理字節類型的包
bufferSize:設置stick處理粘包的緩存空間
  • stick.setReadIntBE(type) => 設置爲大端模式<依據數據包最大值選擇合適type>
setReadIntBE(type)  type:16  包頭長度爲2,short類型
    setReadIntBE(type)  type:32  包頭長度爲4,int類型
  • stick.setReadIntLE => 設置爲小端模式<依據數據包最大值選擇合適type>
setReadIntLE(type)  type:16  包頭長度爲2,short類型
    setReadIntLE(type)  type:32  包頭長度爲4,int類型
  • stick.putData(buffer) => 向stick中推送須要處理粘包的字節流
  • stick.onData(buffer) => 監聽stick解包好的一個個完整消息(包頭+包體),用戶本身的數據存儲在包體中,若是不想處理包頭用msgCenter已經封裝好
  • msgCenter(options) => 可直接發送字符串消息,基於stick封裝,屏蔽stick層須要本身組裝包頭和拆包頭的步驟
options.bufferSize: 設置用戶處理粘包的緩存空間
    options.type:設置包頭爲16位或者32位模式(16|32)
    options.bigEndian: 設置大端、小端字節流模式,默認爲打斷模式,爲false時爲小端模式(true|false)
  • msgCenter.putMsg(msg) => 向消息中心推送字符串消息
  • msgCenter.publish(msg) => 發佈一個消息,返回一個被打包好的buffer(包頭+包體),用戶clent發包時使用
msgCenter.publish('123') 
    => <Buffer 00 03 31 32 33> // 00 03 包長度  31 32 33 字符串123的ascii碼
  • msgCenter.onMsgRecv(msgHandleFun) => 處理通過粘包處理後的消息
msgHandleFun:業務上處理消息的函數
    msgCenter.onMsgRecv(msg => {
        console.log(`recv data: ` + msg.toString())
        ...do something
    })

更新記錄:

  • 設置大端,小端接收,添加setReadIntBE,添加setReadIntLE方法
  • 支持直接發送字符串消息,自動化組裝包頭

使用方法

  • 服務端處理粘包
// 默認client.js 採用 msgCenter.publish('...') 向服務端發消息
    // 如下是服務端收到消息後,進行粘包處理
    const MsgCenter = require('stickpackage').msgCenter
    const msgCenter = new MsgCenter()

    // server 監聽分包後的消息
    msgCenter.onMsgRecv(data => {
        console.log(`recv data: ` + data.toString())
    })

    // 把 tcp server 監聽到的字節流,put到msgCenter中
    msgCenter.putData(Buffer.from([0x00, 0x02, 0x31, 0x32, 0x00, 0x04, 0x31, 0x32, 0x33, 0x34]))
    //=> recv data: 12
    //=> recv data: 1234

  • 發送二進制數據
// 默認client.js 採用 stick 配置的組包式向服務器發送消息
    // 如下是服務端收到消息後,進行粘包處理

    const Stick = require('stickpackage').stick;
    const stick = new Stick(1024).setReadIntBE('16')

    /*
    *  包含兩個數據包,10個字節,包頭爲short,兩個字節:[0x00, 0x02],[ 0x00, 0x04]
    *  數據包1:[0x00, 0x02, 0x66, 0x66]
    *  數據包2:[0x00, 0x04, 0x88, 0x02, 0x11, 0x11]
    */
    const data = Buffer.from([0x00, 0x02, 0x66, 0x66, 0x00, 0x04, 0x88, 0x02, 0x11, 0x11]);

    /*  構造兩個buffer
    *   data2_1包含:  第一個數據包的所有數據,第二個數據包的部分數據    
    *   data2_2包含:  第二個數據包的剩餘數據
    */
    const data2_1 = Buffer.from([0x00, 0x00, 0x00, 0x02, 0x66, 0x66, 0x00, 0x04, 0x88, 0x02, 0x11]);
    const data2_2 = Buffer.from([0x11]);

    // 設置收到完整數據觸發器
    stick.onData(function (data) {
        console.log('receive data,length:' + data.length);
        console.log(data)
    });

    stick.putData(data);        
    stick.putData(data2_1);
    stick.putData(data2_2);  

    //  運行結果:   
    //  receive data,length:4 <Buffer 00 02 66 66>  
    //  receive data,length:6 <Buffer 00 04 88 02 11 11>
    //  receive data,length:2 <Buffer 00 00> receive data, length:4 < Buffer 00 02 66 66> receive data, length:6< Buffer 00 04 88 02 11 11>

案例演示

  • tcp client和tcp server 之間經過stick進行粘包處理通訊,詳細內容見example文件夾
  • [tcp-msg]本demo主要演示TCP中處理粘包的方法,不須要本身組裝包頭,直接發送和接收文本消息,組包解包操做本類庫已經封裝在底層
// Client.js
    const net = require('net')
    const stick = require('../../index')
    const msgCenter = new stick.msgCenter()

    const client = net.createConnection({ port: 8080, host: '127.0.0.1' }, function () {

    const msgBuffer = msgCenter.publish('username=123&password=1234567,qwe')

    client.write(msgBuffer)

})

    client.on('data', function (data) {
        console.log(data.toString())
    })
    client.on('end', function () {
        console.log('disconnect from server')
    })
// Server.js
    const net = require('net')
    const stick = require('../../index')

    const tcp_server = net.createServer(function (socket) {
        const msgCenter = new stick.msgCenter()

        socket.on('data', function (data) {
            msgCenter.putData(data)
        })

        msgCenter.onMsgRecv(function (data) {
            console.log('recv data: ' + data.toString())
        })

        socket.on('close', function (error) {
            console.log('client disconnected')
        })

        socket.on('error', function (error) {
            console.log(`error:客戶端異常斷開: ${error}`)
        })
    })

    tcp_server.on('error', function (err) {
        throw err
    })
    tcp_server.listen(8080, function () {
        console.log('tcp_server listening on 8080')
    })
  • [tcp-buffer]本demo主要演示TCP中直接處理字節流粘包,展現出如何本身組裝包頭包體和解包,如不向本身進行組裝包頭解包操做,請看demo tcp-msg
// Clinet.js
    const net = require('net')

    const client = net.createConnection({ port: 8080, host: '127.0.0.1' }, function () {
        const body = Buffer.from('username=123&password=1234567,qwe')

        // 寫入包頭
        const headBuf = new Buffer(4)
        headBuf.writeUInt32BE(body.byteLength, 0)
        console.log('data length: ' + headBuf.readInt32BE())

        // 發送包頭
        client.write(headBuf)
        // 發送包內容
        client.write(body)
        console.log('data body: ' + body.toString())

    })

    client.on('data', function (data) {
        console.log(data.toString())
    })
    client.on('end', function () {
        console.log('disconnect from server')
    })
// Server.js
    const net = require('net')
    const stick_package = require('../../index').stick

    const tcp_server = net.createServer(function (socket) {
        socket.stick = new stick_package(1024).setReadIntBE('32')
        socket.on('data', function (data) {
            socket.stick.putData(data)
        })

        socket.stick.onData(function (data) {
            // 解析包頭長度
            const head = new Buffer(4)
            data.copy(head, 0, 0, 4)

            // 解析數據包內容
            const body = new Buffer(head.readInt32BE())
            data.copy(body, 0, 4, head.readInt32BE())

            console.log('data length: ' + head.readInt32BE())
            console.log('body content: ' + body.toString())
        })

        socket.on('close', function (error) {
            console.log('client disconnected')
        })

        socket.on('error', function (error) {
            console.log(`error:客戶端異常斷開: ${error}`)
        })
    })

    tcp_server.on('error', function (err) {
        throw err
    })
    tcp_server.listen(8080, function () {
        console.log('tcp_server listening on 8080')
    })

License

MITgithub

Copyright (c) 2017-present, ximen (G.doe)npm

## [源碼地址,喜歡的話請點star,想訂閱點watch](https://github.com/lvgithub/stick)
相關文章
相關標籤/搜索