Node.js第十五篇:Socket.IO


第一章:認識Socket.IO

1.1-WebSocket

傳統的客戶端和服務器通信協議是HTTP:客戶端發起請求,服務端進行響應,服務端從不主動勾搭客戶端

這種模式有個明顯軟肋,就是同步狀態。而實際應用中有大量需要客戶端和服務器實時同步狀態的場景,比如聊天室、股票行情、在線共享文檔等都需要客戶端實時拿到服務器的最新狀態。

針對這種實時同步的需求,一種簡單的方式是輪詢,比如每隔5s發一次http請求去拿服務器最新的狀態數據。但這種方式會存在數據延遲,浪費帶寬等副作用。

更完美的方式是使用WebSocket,瀏覽器原生支持,W3C標准協議,客戶端和服務器建立持久性連接可以互發消息。

1.2-Socket.IO是什么

socket.io 是一個類庫,內部封裝了WebSocket,可以在瀏覽器與服務器之間建立實時通信。

如果某些舊版本的瀏覽器不支持WebSocket,socket.io會使用輪詢代替。另外它還具有可發送二進制消息、多路復用、創建房間等特性,因此相比直接使用原生WebSocket,socket.io是更好的選擇。

開發一個實時應用主要分兩部分:服務端和客戶端,socket.io分別提供了相應的模塊供我們方便地調用。

1.3-什么是Socket

套接字(socket)是一個抽象層,應用程序可以通過它發送或接收數據,可對其進行像對文件一樣的打開、讀寫和關閉等操作。套接字允許應用程序將I/O插入到網絡中,並與網絡中的其他應用程序進行通信。網絡套接字是IP地址與端口的組合。

作用:完成兩個應用程序之間的數據傳輸

1.4-常用的API

官方文檔:https://socket.io/docs/#

服務端

io對象

  • on方法用來監聽事件
    • io.on('connection',socket=>{ })
      • connection對象監聽客戶端連接
      • socket表示當前進入的客戶端socket對象
  • emit('事件名稱',數據) 服務端向所有客戶端發送消息
    • 事件名稱,客戶端或服務端約定好向對方發送消息對方要觸發的事件
    • 數據,傳遞的信息
  • io.to(roomid).emit('事件名稱', 數據) 向指定的屬於同一組的客戶端發送消息
    • roomid 分組名稱表示

socket對象

  • on方法用來監聽事件
    • socket.on('disconnect',()=>{})
      • 若有一個客戶端端口連接,則會觸發該事件
    • socket.on('自定義其他事件名稱',(data)=>{})
      • 自定其他事件名,客戶端或服務端約定好對方發送消息時,用哪個事件觸發
      • data,接收對方發送過來的數據
  • id屬性,socket對象的標識
  • emit('事件名稱',數據) 向對應的客戶端發送消息
    • 事件名稱,客戶端或服務端約定好向對方發送消息對方要觸發的事件
    • 數據,傳遞的信息
  • join(roomid) 加入某個分組
    • roomid,字符串,可以自定義
  • leave(roomid) 從某個分組中脫離
  • socket.broadcast.to(roomid).emit('事件名稱', 數據) 向所有客戶端(同屬一組,除了自己)發送數據

客戶端

io對象

  • io(url) 連接指定的服務端,並返回一個sokcet對象
    • url,服務端連接地址,如:http://localhost

socket對象

  • socket.on('connect', function () {}) 客戶端和服務端建立連接成功后要觸發的事件
  • socket.on('disconnect', function () {}) 客戶端和服務端斷開連接要觸發的事件(比如服務器崩潰)
  • socket.on('自定義其他事件名稱',(data)=>{})
    • 自定其他事件名,客戶端或服務端約定好對方發送消息時,用哪個事件觸發
    • data,接收對方發送過來的數據
  • socket.emit('事件名稱',數據) 客戶端向服務端發送消息

第二章:Socket.IO快速入門

在Node.js中使用Socket.IO

2.1-需求

  • 服務端和客戶端建立連接
  • 客戶端和服務端建立連接,並向服務端發送一條消息
  • 服務端向所有客戶端,發送一條消息
  • 有一個客戶端離線,服務端可以接受到通知

2.2-服務端程序

導入第三方模塊npm install socket.io

const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("fs");
const mime = require("mime");
// 創建服務對象
const app = http.createServer();
// 監聽請求,處理靜態資源
app.on("request", (req, res) => { 
  // 獲取請求的路徑
  let { pathname } = url.parse(req.url, true);
  // 拼接服務器上文件的物理路徑
  let realPath = path.join(__dirname, "public", pathname);
  // 獲取請求的資源類型
  let type = mime.getType(realPath);
  // 讀取服務器本地文件
  fs.readFile(realPath, (err, data) => { 
    if (err) {
      res.writeHead(404,{"Content-type":type+";charset=utf-8"});
      res.end("訪問資源不存在");
      return;
    }
    res.writeHead(200);
    res.end(data);
  });
  

});
// 【重點!!-導入socket.io並和http服務對象關聯】
const io = require('socket.io')(app)
// 【重點!!-檢測客戶端連接進入】
io.on('connection', socket => {
  console.log('一個客戶端進入')
  // 注冊to-server事件,接收客戶端向服務端發送的數據
  socket.on('to-server', (data) => {
    console.log('客戶端說: ' + data)
    // 向對應的客戶端發送數據
    socket.emit('to-client', '我是服務端數據')
    // 向所有在線的客戶端發送數據
    // io.emit('to-client','我是服務端數據')
  })
  // 檢測一個客戶端斷開連接
  socket.on('disconnect', () => {
    console.log('一個客戶端離開')
  })
})
// 開啟端口4000
app.listen(4000);

服務端啟動成功后,客戶端可以通過http://127.0.0.1:4000/socket.io/socket.io.js在客戶端操作socket

2.3-客戶端程序

index.html

  <input type="text" id="message"><button id="btn">發送</button>
  <!-- 導入客戶端socket.io.js -->
  <script src="http://127.0.0.1:4000/socket.io/socket.io.js"></script>
  <script>
    // 創建socket對象,設置要連接的服務器url
    var socket = io('http://127.0.0.1:4000');
    // 注冊connect事件,監聽和服務是否建立了連接
    socket.on('connect', function () {
      console.log('客戶端和服務建立了連接')
    })
    // 注冊disconnect事件,監聽和服務是否斷開連接
    socket.on('disconnect', function () {
      console.log('客戶端和服務端斷開連接了');
    })
    // 注冊to-client事件,監聽服務端向客戶端傳送的數據
    socket.on('to-client', function (data) { 
      console.log('服務端說:' + data); 
    })
    // 點擊按鈕向服務端發送數據
    btn.onclick = function() {
      var val = message.value
      socket.emit('to-server',val)
    }
  </script>

2.4-測試

  1. 打開多個客戶端:http://127.0.0.1:4000/index.html
  2. 在控制台查看服務端發送的消息
  3. 在服務端控制台中查看客戶端發送的消息

第三章:Express中使用Socket.IO

3.1-基本使用

服務端程序

const express = require('express')
const path = require('path')
const app = express()
const statiPath = path.join(__dirname, './public')
app.use(express.static(statiPath))

// 【重點-通過http模塊Server方法獲取關聯Express的服務對象】
const server = require('http').Server(app);
// 【重點-導入socket.io模塊獲取io對象並關聯http服務對象】
const io = require('socket.io')(server);

// 【重點-檢測客戶端和服務端是否連接成功】
io.on('connection', socket => {
  console.log('有一個客戶端連接成功')
  // 監聽當前客戶端向服務端發送的數據
  socket.on('message', data => {
    console.log('客戶端說:' + data)
    // 服務端向所有客戶端發送消息
    io.emit('message',data)
  })
})

// 【重點-使用server監聽80端口】
server.listen(80)

客戶端程序

index.html

  <input type="text" id="msg"><button id="btn">發送</button>
  <script src="http://localhost/socket.io/socket.io.js"></script>
  <script>
    // 客戶端創建socket對象,並配置連接服務器url
    var socket = io('http://localhost')
    // 監聽服務端發送過來的數據
    socket.on('message',function(data){
      console.log(data)
    })
    // 點擊按鈕向服務端發送數據
    btn.onclick = function(){
      socket.emit('message',msg.value)
    }
  </script>

3.2-發送文字和圖片

服務端程序

const express = require('express')
const path = require('path')
const app = express()
const statiPath = path.join(__dirname, './public')
app.use(express.static(statiPath))

// 【重點-通過http模塊Server方法獲取關聯Express的服務對象】
const server = require('http').Server(app);
// 【重點-導入socket.io模塊獲取io對象並關聯http服務對象】
const io = require('socket.io')(server);

// 【重點-檢測客戶端和服務端是否連接成功】
io.on('connection', socket => {
  console.log('有一個客戶端連接成功')
  // 監聽當前客戶端向服務端發送的數據-文本消息
  socket.on('message', data => {
    console.log('客戶端說:' + data)
    // 服務端向所有客戶端發送消息
    io.emit('message',data)
  })
  // 監聽當前客戶端向服務端發送的數據-文件消息
  socket.on('image', data => {
    // 向其他客戶端發送文件
    io.emit('image', data);
  })
})

// 【重點-使用server監聽80端口】
server.listen(80)

客戶端程序

  <div class="talk">
    <!-- 聊天記錄 -->
    <div class="box">
      <ul id="ul"></ul>
    </div>
    <!-- 發送文本消息 -->
    <p>
      <textarea id="msg" placeholder="請輸入內容"></textarea>
      <!-- 按鈕 -->
      <button id="btn">發送</button>
      <button id="btn2">圖片</button>
      <input type="file" style="display:none" id="fileDom">
    </p>

  </div>

  <script src="http://localhost/socket.io/socket.io.js"></script>
  <script>
    // 客戶端創建socket對象,並配置連接服務器url
    var socket = io('http://localhost')
    // 監聽服務端發送過來的數據-文本
    socket.on('message', function (data) {
      var li = document.createElement('li');
      li.innerText = data;
      ul.appendChild(li)
    })
    // 監聽服務端發送過來的數據-文本
    socket.on('image', function (data) {
      var li = document.createElement('li');
      li.innerHTML = '<img src="'+data+'">';
      ul.appendChild(li)
    })
    // 點擊按鈕向服務端發送數據-文字
    btn.onclick = function () {
      socket.emit('message', msg.value)
    }
    // 點擊按鈕向服務端發送數據-圖片
    btn2.onclick = function () {
     fileDom.click()
    }
    // 上傳事件觸發
    fileDom.onchange = function() {
     // 獲取讀取的文件
     var file = fileDom.files[0];
     // 創建fileReader對象
     var reader = new FileReader();
     // 讀取文件內容
     reader.readAsDataURL(file)
     // 讀取完畢后,發送到服務端
     reader.onload = function(ev){
       // console.log(reader.result)
       // 發送文件數據
       socket.emit('image',reader.result)
     }
    }
  </script>

第四章:Koa中使用Socket.IO

導入第三方模塊 npm install koa-socket-2

參考文檔:https://www.npmjs.com/package/koa-socket-2

服務端程序

const koa = require('koa')
const router = require('koa-router')()
const path = require('path')
const static = require('koa-static')
// 【導入koa-socket-2模塊】
const IO = require('koa-socket-2');

// 創建koa服務對象
const app = new koa()
// 【創建IO對象】
const io = new IO();
// 【和koa服務對象關聯】
io.attach(app)

// 定義roomid,實現分組
let roomid = 'group';

// 【監聽客戶端連接服務】
app._io.on('connection', socket => {
  console.log('有新的客戶端進入')
  // 新的客戶端socket加入組中
  socket.join(roomid);
  // 【監聽客戶端發送的數據】
  socket.on('message', data => {
    console.log('客戶端發送的數據是:' + data)
  })
  // 服務端向當前客戶端發送數據
  socket.emit('message', '對您廣播:您好,同志')
  // 服務端向所有客戶端發送數據
  app._io.emit('message', '全員廣播:同志們,咱們大家好')
  // 服務端向所有客戶端(同屬一組,除了自己)發送數據
  socket.broadcast.to('group').emit('message', '小組中的朋友們,大家好')
  // 服務端向所有客戶端(同屬一組,包括了自己)發送數據
  app._io.to('group').emit('message', '小組中的朋友們,大家好2')
  // 檢測一個客戶端離線
  socket.on('disconnect', () => {
    // socket.id 獲取socket的唯一標識
    console.log('id為' + socket.id + '客戶端離線了');
  })
});

// 配置路由和靜態資源
app.use(router.routes())
app.use(router.allowedMethods()); 
app.use(static(path.join(__dirname,'./public')))

app.listen(80)

客戶端程序

  <input type="text" id="text">
  <button id="btn">向服務端發送數據</button>
  <script src="http://localhost/socket.io/socket.io.js"></script>
  <script>
    // 連接服務端,並創建客戶端socket對象
    var socket = io('http://localhost');

    // 點擊按鈕向服務端發送數據
    btn.onclick = function() {
      var val = text.value;
      socket.emit('message',val);
    }

    // 監聽服務端發送的數據
    socket.on('message',function(data){
      console.log('服務端說:' + data);
    })
  </script>

第五章:案例-Open聊天室

5.1-需求

登錄界面

  1. 用戶進入登錄頁面
  2. 用戶輸入昵稱,點擊進入聊天室
  3. 后端檢測,該用戶是否存在(是否已經存在有該昵稱的socket)
  4. 存在,則提示用戶更換用戶名
  5. 不存在,則允許用戶進入聊天室,並在后端系統中保存該用戶的socket

聊天界面

  1. 功能1:展示出所有在線用戶
  2. 功能2:群聊
  3. 功能3:私聊
  4. 功能4:上線消息提醒
  5. 功能5:發送圖片
  6. 功能6:新消息提醒

5.2-頁面交互流程圖

通過流程圖直觀了解業務

5.3-代碼下載

代碼沒有做詳細優化,后續會更新

https://gitee.com/lpl666/openliaotianshi.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM