如何用tcp寫一個聊天室

渣渣的生存之道發表於2018-07-03

tcp不能通過瀏覽器訪問,因為他並不知道協議的特點,如果用windows開發,需要安裝'PuTTY,xsel',mac不需要

tcp屬於傳輸層 => 資料傳遞 ,傳輸層有很多協議類似於udp等

如何用tcp寫一個聊天室
具體請參考七層協議

我們最常用的http屬於應用層,tcp屬於傳輸層,也就是說應用層建立在傳輸層上,在node中為了專門實現某種傳輸手段,提供了一個tcp包

windows需要安裝telnet 控制皮膚=>程式 => telnet√一下 Telnet localhost 3000(不建議)

Telnet協議是TCP/IP協議族中的一員,是Internet遠端登陸服務的標準協議和主要方式。它為使用者提供了在本地計算機上完成遠端主機工作的能力。在終端使用者的電腦上使用telnet程式,用它連線到伺服器。終端使用者可以在telnet程式中輸入命令,這些命令會在伺服器上執行,就像直接在伺服器的控制檯上輸入一樣。

我們用putty (支援中文)

如何用tcp寫一個聊天室

安裝後需要配置常用埠.

//node實現tcp協議  給我們提供了一個包 net模組
//tcp 互動也要有服務端和客戶端
//客戶端: 可以通過任意一個埠號給服務發請求
//服務端:接受並且響應
let net = require('net');

//建立服務端
//socket套接字 會話,http有請求和響應
// let server = net.createServer(function(socket){
//     //請求到來的時候執行以下
//     console.log('客戶端連結了服務端');
    
// })
//等同於
let server = net.createServer();
server.on('connection',function(socket){
    //請求到來的時候執行以下
    console.log('客戶端連結了服務端');
})
// (埠號,伺服器地址預設不寫,511(可以接收多少個請求))
//監聽成功會自動走回撥
let port = 3000; 
server.listen(port,function(){
    console.log(`server start ${port}`);
})
複製程式碼

我們在服務端建立埠,同時在cmd上執行,會報錯

如何用tcp寫一個聊天室

這個我們最常見的埠衝突,我們想在埠衝突的時候讓埠自增1

我們加一行程式碼就可以解決

server.on('error',function(err){
    if(err.code == 'EADDRINUSE'){
        server.listen(++port)
    }
})
//設定最大連線數
server.maxConnections = 2
複製程式碼

socket 是一個Duplex 就是雙攻流,可讀可寫 即客戶端一個,服務端一個,兩個是同一個人,

server.on('connection',function(socket){
    //請求到來的時候執行以下
    socket.write("歡迎光臨")
})
複製程式碼

設定當客戶端請求時候迴應如圖

如何用tcp寫一個聊天室

socket.end();//關閉客戶端
server.close();//關閉自己  如果觸發了close事件就不會在觸發新的請求了
server.unref(); //表示關閉  沒有客戶端連結會自己關閉,不會觸發close事件
//接收訊息,可以通過流的方式接收到資料
socket.setEncoding('utf8')
socket.on('data' , function(data){
    console.log(data)
});
複製程式碼

如何用tcp寫一個聊天室

現在我們來實現一個聊天室, 每次連結服務端socket不是同一個人

  1. 提示當前線上人數多少,總共能容納多少人
  2. 提示請輸入使用者名稱
  3. 廣播訊息
//建立一個服務
let net = require('net');
let server = net.createServer();
//client儲存使用者資訊
let client = {};
//每次連結伺服器 socket每次都會產生新的
server.on('connection',function(socket){ 
    //設定最大連線數,server.getConnections每次連線返回給客戶端資訊, socket.write讀流,通過流的方式接收到資料
    server.maxConnections = 4;
    server.getConnections(function(err,count){
        socket.write(`歡迎光臨,當前人數為${count},總容納數${server.maxConnections}人\r\n`);
        socket.write(`請輸入使用者名稱:\r\n`);
    })
    socket.setEncoding('utf8')
    let nickName;//儲存使用者名稱
    socket.setEncoding('utf8')
    socket.on('data' , function(chunk){//監聽輸入
        chunk = chunk.replace(/\r\n/,'');//chunk自帶換行回車
        if(nickName){//把說的內容給別人看
            Broadcast(nickName, chunk)
        }else{
            nickName = chunk;
            client[chunk] = socket;
        }
    });
})
function Broadcast(nickName, chunk){
   Object.keys(client).forEach(nick => {
       if(nickName != nick){
            client[nick].write(`${nickName}:${chunk}\r\n`)
       }
   });
}
//監聽伺服器關閉事件
server.on('close',function(){
    console.log("伺服器關閉")
})
複製程式碼

下面是我們寫的全部內容

//node實現tcp協議  給我們提供了一個包 net模組
//tcp 互動也要有服務端和客戶端
//客戶端: 可以通過任意一個埠號給服務發請求
//服務端:接受並且響應
let net = require('net');

//建立服務端
//socket套接字 會話,http有請求和響應
// let server = net.createServer(function(socket){
//     //請求到來的時候執行以下
//     console.log('客戶端連結了服務端');
    
// })
let server = net.createServer();

let client = {};
//每次連結伺服器 socket每次都會產生新的
server.on('connection',function(socket){ 
    //設定最大連線數
    server.maxConnections = 4;
    server.getConnections(function(err,count){
        socket.write(`當前人數為${count},總容納數${server.maxConnections}人\r\n`);
        socket.write(`請輸入使用者名稱:\r\n`);
    })
    //請求到來的時候執行以下  socket是一個Duplex 雙攻流,可讀可寫
    socket.write("歡迎光臨");
    //讀流,通過流的方式接收到資料
    socket.setEncoding('utf8');
    //監聽輸入
    let nickName;
    //儲存使用者名稱
    socket.setEncoding('utf8')
    socket.on('data' , function(chunk){
        chunk = chunk.replace(/\r\n/,'');
        if(nickName){
            //把說的內容給別人看
            Broadcast(nickName, chunk)

        }else{
            //chunk自帶換行回車
            nickName = chunk;
            client[chunk] = socket;
        }
        //server.close();//關閉自己 如果觸發了close事件就不會在接收新的請求了,
        //server.unref(); //表示關閉 沒有客戶端,所有人都退出,連結會自己關閉,不會觸發close事件
    });
    //end  服務端把客戶端關掉了,,,
    //socket.end();
})
function Broadcast(nickName, chunk){
   Object.keys(client).forEach(nick => {
       if(nickName != nick){
            client[nick].write(`${nickName}:${chunk}\r\n`)
       }
   });
}
// (埠號,伺服器地址預設不寫,511(可以接收多少個請求))
//監聽成功會自動走回撥
let port = 3000; 
server.listen(port,function(){
    console.log(`server start ${port}`);
})

//如果埠被佔用,我們重啟一個埠號
server.on('error',function(err){
    if(err.code == 'EADDRINUSE'){
        server.listen(++port)
    }
})
server.on('close',function(){
    console.log("伺服器關閉")
})

複製程式碼

多功能聊天室

  • 如果說話的時候富裕一些特殊意義,可以自己增加一些標識

  • look: 看所有的線上人數

  • say:zs: 私聊

  • rename: 重新命名

  • all: 廣播

  • 我們有個物件 clinet 當前的人{127.0.0.1}

  • 當然都是本地的,所以每個人都是這個,但是不可能同一個埠發出兩個socket ,假設第一個埠是{127.0.0.1:8080},另一個是8081,並且我們放歌物件,預設匿名,一級當前是哪個sokect

  • {127.0.0.1:8080:{nickname:'匿名',socket:socket}}

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`);
        socket.write(`請輸入使用者名稱:\r\n`);
    })
    let key = socket.remoteAddress + socket.remotePort; // 遠端地址和遠端埠號
    console.log(key);
    socket.setEncoding('utf8');
    client[key] = {nickName:'匿名',socket:socket}
    socket.on('data' , function(chunk){
        chunk = chunk.replace(/\r\n/,'');
        let char = chunk.split(':')[0];
        let content = chunk.split(':')[1];
        switch(char){
            case 'look':
                showList(socket);
                break;
            case 'say':
                private(content , chunk.split(':')[2],client[key].nickName);
                break;
            case 'rename':
                rename(key, content);
                break;
            case 'all':
                Broadcast(key , chunk, client[key].nickName)
                break;
        }
        // key = socket.remoteAddress + socket.remotePort 
        // client[key] = {nickName:'匿名',socket:socket}
        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;
        }
        function private(nickName, content , n){
            let s;
            Object.keys(client).forEach(key => {
                if(client[key].nickName === nickName){
                    s = client[key].socket
                }
            });
            s.write(`${n}: ${content}\r\n`)
        }
        function Broadcast(nick , chunk , nickName){
            Object.keys(client).forEach(key => {
                console.log(key,nick)
                if(key != nick){
                    client[key].socket.write(`${nickName}:${chunk}\r\n`)
                }
            });
        }
        r.unref();
    });
})
let port = 3000; 
server.listen(port,function(){
    console.log(`server start ${port}`);
})

複製程式碼

tcp寫服務端

let net = require('net');
let server = net.createServer(function(socket){
    socket.write('hello');
    socket.on('data',function(data){
        console.log(data);
    })
}).listen(3000)

//模擬客戶端,並不是通過伺服器
let net = require('net');
let socket = net.createConnection({port:3000},function(){
    //同一次請求sockect就是同一個
    socket.write('hi');
    socket.on('data',function(data){
        console.log(data);
    })
})


複製程式碼

如何用tcp寫一個聊天室

相關文章