Node.js中TCP及聊天室的實現

前言

OSI網絡模型分七層,由下至上分別爲:物理層數據鏈路層網絡層傳輸層會話層表示層應用層,TCP協議處於傳輸層。緩存

Node.js中TCP

Node.js中的 net 模塊提供了對TCP協議的封裝,使用 net 模塊能夠輕鬆的構建一個TCP服務器,或構建一個鏈接TCP服務器的客戶端。bash

net模塊建立一個服務器

let net = require('net')

let server = net.createServer()
server.on('connection', function (socket) {
    // socket套接字 會話,http有請求 響應
    console.log('connection success')
})
server.listen(3000, function () {
    console.log('server start at 3000')
})
複製代碼

這樣就建立了一個簡單的服務。啓動在3000端口,而且會監聽客戶端的鏈接。socket表示套接字,是一個可讀可寫的雙工流,能夠理解爲客戶端和服務端之間的會話。服務器

接收數據網絡

socket.setEncoding('utf8')
socket.on('data', function (data) {
    console.log(data)
})
複製代碼

響應數據socket

socket.write('nice to meet you')
複製代碼

關閉客戶端tcp

socket.end('end...') // 關閉客戶端
複製代碼

關閉服務端

server.close(); // 若是觸發close事件就不會再接收新的請求了
server.unref(); // 也表示關閉 ,沒有客戶端鏈接會本身關閉(不會觸發close事件)

server.on('close', function () {
    console.log('server closed')
})
複製代碼

close調用後, 服務端再也不接收新的請求,當沒有客戶端鏈接,會觸發close事件,並關閉服務端函數

unref調用後,服務端繼續接受新的請求,當沒有客戶端鏈接,不會觸發close事件,並關閉服務端。測試

設置最大鏈接數

server.maxConnections = 2; // 設置最大鏈接數,超過數量不能鏈接
複製代碼

聊天室的實現

有了上面的知識,咱們能夠動手實現一個聊天室。ui

需求

  • 當前用戶在線數,最多可鏈接的用戶數;
  • 輸入 l:, 查看當前用戶列表;
  • 輸入 s:zs: hello, 向張三發送消息;
  • 輸入 r: ls, 給本身重命名爲ls;
  • 輸入 b: nice to meet you, 向其餘用戶廣播消息

建立服務

let net = require('net');

let server = net.createServer((socket) => {
    let key = socket.remoteAddress + socket.remotePort
    console.log(key)
})
server.listen(3000, function () {
    console.log('server start at 3000')
})
複製代碼

緩存客戶端信息

let client = {}
let server = net.createServer((socket) => {
    // 顯示歡迎信息
    server.maxConnections = 3;
    server.getConnections(function (err, count) {
        socket.write(`歡迎到來,當前用戶 ${count},總容納 ${server.maxConnections}\r\n`)
    })
    // 緩存用戶
    let key = socket.remoteAddress + socket.remotePort
    client[key] = {nickname: '匿名',socket}
})
複製代碼

緩存客戶端信息到 client中,以客戶的ip+port做爲key值,nickname默認爲匿名,並保存回話socket。spa

處理客戶端輸入的指令

socket.setEncoding('utf8')
    socket.on('data', function (chunk) {
        chunk = chunk.replace(/\r\n/,'')
        let [command, target, content] = chunk.split(':')
        switch (command) {
            case 'l':  //查看用戶列表
                showList(socket);
                break;
            case 's':  //私聊
                charTo(target,content, client[key].nickname);
                break;
            case 'r': //重命名
                rename(key, target);
                break;
            case 'b': //想其餘用戶廣播
                broadcast(key, target, client[key].nickname);
                break;
            default:
                break;
        }
    })
複製代碼

處理函數的實現

showList 函數的實現

function showList(socket) {
    let users = []
    Object.values(client).forEach(user => {
        users.push(user.nickname)
    })
    socket.write(`當前用戶列表:\r\n ${users.join('\r\n')} \r\n`)
}
複製代碼

charTo函數的實現

function charTo(target, content, source) {
    let targetSocket;
    Object.values(client).forEach(user => {
        if(user.nickname === target) {
            targetSocket = user.socket
        }
    })
    targetSocket.write(`${source}${content}\r\n`)
}
複製代碼

rename函數的實現

function rename(key, content) {
    client[key].nickname = content;
    client[key].socket.write(`重命名爲: ${content}\r\n`)
}
複製代碼

broadcast函數的實現

function broadcast(key, content, nickname) {
    Object.keys(client).forEach(user => {
        if(user !== key) {
            client[user].socket.write(`${nickname}: ${content}\r\n`)
        }
    })
}
複製代碼

成果檢驗

咱們的建議聊天室就作好了,你們能夠使用 PuTTY這個軟件做爲客戶端發送tcp請求;

下面是我測試的結果。

結語

本身實現一個聊天室的過程仍是挺有意思的,歡迎喜歡搗鼓的同窗多多交流。

相關文章
相關標籤/搜索