1. 程式人生 > >1025_(即時通訊)使用 Socket.IO 開發聊天室

1025_(即時通訊)使用 Socket.IO 開發聊天室

使用 Socket.IO 開發聊天室

前言

Socket.IO 是一個用來實現實時雙向通訊的框架,其本質是基於 WebSocket 技術。

我們首先來聊聊 WebSocket 技術,先設想這麼一個場景:

· 使用者小A,打開了某個網站的充值介面,該介面上有一個付款的二維碼。

· 當小A 用某寶的 APP 掃碼付款之後,網頁要自動跳轉到付款成功的介面。最簡單的方法就是網頁每隔一段時間就請求一次伺服器——“怎麼樣?那貨付款沒有?”,“怎麼樣?還沒付嗎?”,“怎麼樣?這次總該付了吧”。

· 當接收到服務端返回確認付款的標識後,頁面再進行跳轉。

這種定時對服務端傳送 HTTP 請求的技術也被稱為“輪詢”。輪詢的缺點也顯而易見,短時間內的多次訪問無疑會對伺服器造成很大壓力。

  後來,人們發明了長輪詢技術。這與短輪詢的區別在於,每次瀏覽器請求伺服器後,伺服器並不會立即返回“使用者還未付款”的結果,而是一直將 HTTP 保持在掛起狀態,只有當用戶付款後才會返回給瀏覽器 “使用者已經付款” 的結果。當然,如果使用者在規定時間內仍未付款還是會斷開此次連線,之後瀏覽器再開始傳送下一輪的長連線。相比短輪詢,長輪詢有了一定的優勢,但這還是不夠好。直到 HTML5 裡 WebSocket 技術的誕生。

  WebSocket,即 Web 瀏覽器與 Web 伺服器之間的全雙工通訊標準。最初 WebSocket 只是作為 HTML5 標準的一部分,而後來卻逐漸變成了獨立的協議標準。一旦瀏覽器與伺服器建立起 WebSocket 協議的通訊連線,之後所有的通訊都依靠該協議進行。不論是伺服器還是瀏覽器,任何一方都能向對方傳送報文。通訊過程中可相互發送 JSON、HTML或圖片等任意格式的資料。

   關於 WebSocket 的原生 API 如何使用,本文不贅述了。 這裡主要聊聊關於 Socket.IO 這個庫的使用方式。Socket.IO 與 ws 等其他基於 WebSocket 通訊的庫相比,它的好處在於,當瀏覽器支援 WebSocket 技術時它能正常使用 WebSocket 來工作,當瀏覽器不支援 WebSocket 時,它能平穩退化成輪詢進行工作。

 

首先放出自己做的聊天室 DEMO 原始碼,將原始碼克隆到本地,執行下面兩個命令:

npm install
npm start

 接著用瀏覽器訪問 localhost:8000 即可,實際介面如下圖:

     

 

Socket.IO 在客戶端與服務端二者均有一套 API,我們舉幾個常用的進行說明。

服務端

new Server

因為 WebSocket 協議是建立在 HTTP 協議之上的,所以在建立 WebSocket 服務時需要呼叫 http 模組並呼叫其下的 createServer 方法,將生成的 server 作為引數傳入 socket.io 的方法中。

var server = require('http').createServer(),
    io = require('socket.io')(server);

server.listen(8000);

 

io.onconnection(socket)

io 的 connection 事件表示客戶端與伺服器成功建立連線,它會接收一個回撥函式,該回調函式會接收一個 socket 引數。

io.on('connection', (socket) => {
    console.log(socket);
});

 

io.emit(EventName, param1, param2...)

io.emit 方法用於向伺服器傳送訊息,其第一個引數表示自定義的資料名,後面表示需要配合事件傳入的引數。

io.on('connection', (socket) => {
    io.emit('server message', {msg: 'Hello World.'})
});

 

socket.on(EventName, callback)

socket.on 方法用於接收客戶端傳送來的訊息,EventName 引數為客戶端自定義的事件名,callback 為回撥函式,回撥函式接收的引數即客戶端傳遞來的引數。

io.on('connection', (socket) => {
    socket.on('client message', (data) => {
        console.log(data);
    });
});

 

socket.broadcast.emit() 

socket.broadcast.emit() 方法表示向除了自己以外的客戶端傳送訊息。舉個例子,當我們輸入 “Message” 點擊發送,只需要將 “Message” 通過伺服器傳送給其他客戶端用於顯示,而本地只需要將 “Message” 通過 js 程式碼新增進聊天視窗即可,而不需要經過伺服器。

io.on('connection', (socket) => {
    socket.broadcast.emit('server message', {
        msg: 'Hello World.'
    });
});

 

socket.ondisconnect

socket 的 disconnect 事件表示客戶端與服務端斷開連線。

io.on('connection', (socket) => {
    socket.on('disconnect', () => {
        console.log('連線已斷開...');
    });
});

 

客戶端

首先需要引入 socket.io 模組中的 socket.io.js 檔案。

<!Doctype HTML>
<html>
<head>
    <title>Socket.IO chat</title>
    <script src="/socket.io/socket.io.js"></script>
</head>
</html>

引入成功後,就可能通過 io() 生成客戶端所用的 socket 物件。

var socket = io();

 

socket.emit(EventName, param1, param2, .....)

socket.emit 方法用於客戶端向服務端傳送訊息,服務端與之對應的是 socket.on 方法來接收訊息。

複製程式碼

//客戶端
var socket = io();
socket.emit('client message', {
    msg: 'Hello Server'
});

//服務端
io.on('connection', (socket) => {
    socket.on('client message', (data) => {
        console.log(data.msg); // Hello Server
    });
});

複製程式碼

 

socket.on(EventName)

socket.on 方法用於接收服務端發來的訊息

複製程式碼

//服務端
io.on('connection', (socket) => {
    io.emit('server message', {
        msg: 'Hello Client'
    });
});

//客戶端
var socket = io();
socket.on('server message', (data) => {
    console.log(data.msg); // Hello Client
});

複製程式碼

 

介紹完 Socket.IO 的 API,下面開始嘗試寫聊天室的 DEMO。

首先建立目錄如下:

其中 server.js 中儲存著服務端的程式碼,client 資料夾中儲存著客戶端程式碼,包括 HTML、CSS、JS、圖片等。

編寫 server.js 如下:

複製程式碼

var express = require('express'),
    app = express(),
    server = require('http').createServer(app),
    io = require('socket.io')(server),
    port = process.env.PORT || 8000;

//通過 express 載入前端部分的靜態資原始檔
app.use(express.static(__dirname + '/client'));

io.on('connection', (socket) => {
   //通過 socket.id 建立簡易的使用者名稱
    let user = '遊客' + socket.id.substring(0, 6);
    
    //通知使用者進入
    io.local.emit('user conncet', user + '進入聊天室');

    //接收從客戶端傳送來的訊息,拼上訊息釋出者的名字後,廣播給其他客戶端
    socket.on('client message', (data) => {
        data.author = user;

        //將訊息廣播給除自己以外的客戶端
        socket.broadcast.emit('server message', data);
    });

    //通知使用者離開
    socket.on('disconnect', () => {
        io.local.emit('user disconnect', user + '離開聊天室');
    });
});

server.listen(port, () => {
    console.log('listening on %d...', port);
});

複製程式碼

前端部分程式碼省略,這裡只簡單寫一個傳送訊息的方法,具體的程式碼請參考 DEMO 原始碼

複製程式碼

var socket = io();

function sendMsg() {
    var message = document.querySelector('#input');
    
    socket.emit('client message', {
        text: message,
        time: new Date()
    });

    document.querySelector('#content').innerHTML += '<div class="msgList">'+message+'</div>';
}

複製程式碼

如上,當執行 server.js 檔案後,在瀏覽器位址列中輸入 localhost:8000 就可以看到聊天室的介面了。 這樣我們就完成了一個簡易的聊天室,它包括如下功能:

· 傳送/接收訊息

· 通知使用者進入/離開

· 為進入聊天室的使用者命名,名稱唯一

有興趣的朋友還可以嘗試自行完成如下功能:

· 使用者登陸

· 檢視所有線上使用者

· @ 功能

···