如何用tcp寫一個聊天室

tcp不能經過瀏覽器訪問,由於他並不知道協議的特色,若是用windows開發,須要安裝'PuTTY,xsel',mac不須要javascript

tcp屬於傳輸層 => 數據傳遞 ,傳輸層有不少協議相似於udp等java

具體請參考 七層協議

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

windows須要安裝telnet 控制面板=>程序 => telnet√一下 Telnet localhost 3000(不建議)windows

Telnet協議是TCP/IP協議族中的一員,是Internet遠程登錄服務的標準協議和主要方式。它爲用戶提供了在本地計算機上完成遠程主機工做的能力。在終端使用者的電腦上使用telnet程序,用它鏈接到服務器。終端使用者能夠在telnet程序中輸入命令,這些命令會在服務器上運行,就像直接在服務器的控制檯上輸入同樣。

咱們用putty (支持中文)瀏覽器

安裝後須要配置經常使用端口.服務器

//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上運行,會報錯socket

這個咱們最多見的端口衝突,咱們想在端口衝突的時候讓端口自增1tcp

咱們加一行代碼就能夠解決ui

server.on('error',function(err){
    if(err.code == 'EADDRINUSE'){
        server.listen(++port)
    }
})
//設置最大鏈接數
server.maxConnections = 2
複製代碼

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

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

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

socket.end();//關閉客戶端
server.close();//關閉本身 若是觸發了close事件就不會在觸發新的請求了
server.unref(); //表示關閉 沒有客戶端連接會本身關閉,不會觸發close事件
//接收消息,能夠經過流的方式接收到數據
socket.setEncoding('utf8')
socket.on('data' , function(data){
    console.log(data)
});
複製代碼

如今咱們來實現一個聊天室, 每次連接服務端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);
    })
})


複製代碼

相關文章
相關標籤/搜索