第一章:認識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
服務端
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對象
- i
o(url)
連接指定的服務端,並返回一個sokcet對象- url,服務端連接地址,如:
http://localhost
- url,服務端連接地址,如:
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-測試
- 打開多個客戶端:http://127.0.0.1:4000/index.html
- 在控制台查看服務端發送的消息
- 在服務端控制台中查看客戶端發送的消息
第三章: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-需求
登錄界面
- 用戶進入登錄頁面
- 用戶輸入昵稱,點擊進入聊天室
- 后端檢測,該用戶是否存在(是否已經存在有該昵稱的socket)
- 存在,則提示用戶更換用戶名
- 不存在,則允許用戶進入聊天室,並在后端系統中保存該用戶的socket
聊天界面
- 功能1:展示出所有在線用戶
- 功能2:群聊
- 功能3:私聊
- 功能4:上線消息提醒
- 功能5:發送圖片
- 功能6:新消息提醒
5.2-頁面交互流程圖
通過流程圖直觀了解業務
5.3-代碼下載
代碼沒有做詳細優化,后續會更新