websocket工作機制
- WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議臭挽。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動發(fā)送信息給客戶端蜡饵。websocket鏈接建立后雏搂,服務(wù)端和客戶端可通過連接通道自由通信渗勘。wx協(xié)議也有類似于https的wss協(xié)議狰晚,也是多了一層TLS協(xié)議盏筐。
- websocket具體詳細的內(nèi)容就不多做介紹了收厨,因為我這次使用的socket.io框架晋柱,寫起來的語法和原生websocket語法還是有很大差別的。
Socket.io簡介
- socket.io可以看做是一個對websocket進行了封裝的一個框架诵叁,目的也是為了實現(xiàn)客戶端和服務(wù)端的全雙工通信雁竞。socket.io的底層是engine.io,engine.io在不支持websocket的瀏覽器中拧额,會使用ajax的長輪詢實現(xiàn)效果碑诉。
- 具體過程,瀏覽器和服務(wù)端首先建立xhr鏈接侥锦,第一次握手后进栽,服務(wù)端返回瀏覽器一個upgrades字段,告訴前端可以升級為websocket協(xié)議恭垦,socket.io客戶端的框架會進行環(huán)境監(jiān)測支持websocket的話快毛,就會將xhr鏈接升級為websocket。建立起一個websocket連接后番挺,服務(wù)器和瀏覽器之間還會定期的ping-pong來確定網(wǎng)絡(luò)鏈接是否正常唠帝。
具體實現(xiàn)
- 再來看下具體實現(xiàn)的代碼如何寫,socket.io官網(wǎng)上面的文檔和教程還是挺詳細的玄柏,而我們要實現(xiàn)這個多人聊天室也只需要其中一些比較基本的api即可襟衰。
服務(wù)端部署
- 服務(wù)端我用的node,直接用Npm安裝socket.io粪摘,可以用socket.io直接搭建服務(wù)器瀑晒,也可以在原有的express/koa上部署绍坝,我之前用的express,這里就是將socket.io添加到原有的express服務(wù)器上:
let app = new (require('express'))()
const server = app.listen(8081, () => {
let peopleCounts = 0
let io = require('socket.io')(server)
io.on('connection', function (client) {
// 這里的作用域是局部的苔悦,針對一個窗口發(fā)起的ws鏈接
++peopleCounts
let uname = ''
let uColor = require('./methods/util').getColor()
client.on('disconnect', function (e) {
--peopleCounts
io.emit('sys message', {
data: `系統(tǒng)消息: "${uname}"離開聊天室`,
counts: peopleCounts
})
})
require('./methods/getNickName')()
.then(name => {
uname = name
io.emit('initUser', {
name,
color: uColor
})
io.emit('sys message', {
data: `系統(tǒng)消息: "${name}"進入聊天室`,
counts: peopleCounts
})
})
.catch(e => { console.log(e) })
client.on('chat message', function (data) {
io.emit('message from server', data)
})
})
console.log('Example app listening on port 8081!')
})
- 在express服務(wù)器搭建成功的回調(diào)函數(shù)里綁定socket.io轩褐,這里的io變量就是表示socket.io的服務(wù)端。
- .on方法監(jiān)聽事件间坐,這些事件來自建立websocket連接時或者瀏覽器端主動觸發(fā)灾挨。
- .emit觸發(fā)事件邑退,前端可以監(jiān)聽這些事件竹宋。比如一個客戶端連接到服務(wù)端時,服務(wù)端會生成一個隨機昵稱(getNickName方法非必要)地技,然后返回給客戶端蜈七。
客戶端部署
import io from 'socket.io-client'
export default {
name: 'cheatRoom',
data () {
return {
description: '',
nickName: '',
counts: 1,
msg: [],
color: ''
}
},
created () {
window.vm = this
this.initSocket()
},
methods: {
initSocket () {
const socket = io(this._config().preurl)
socket.on('connect', () => {
this.socket = socket
})
// 這里api語法類似于jQuery,支持once監(jiān)聽一次,off取消監(jiān)聽
// [https://socket.io/docs/client-api/#socket-on-eventname-callback](https://socket.io/docs/client-api/#socket-on-eventname-callback)
socket.once('initUser', ({name, color}) => {
this.nickName = name
this.color = color
})
socket.on('sys message', ({ data, counts }) => {
this.counts = counts
this.msg.push({
description: data,
sys: true
})
})
socket.on('message from server', (item) => {
item.sys = false
this.msg.push(item)
})
socket.on('disconnect', () => {
this.showAlert({
msg: '服務(wù)端斷開連接,請聯(lián)系管理員Token',
autoClose: false,
type: 'error'
})
})
},
loadAction () {
let { description, nickName, color } = this
if (this.description.length === 0) {
return
}
this.socket.emit('chat message', {
description,
nickName,
color
})
this.description = ''
}
- 前端需要用npm安裝socket.io-client莫矗,不能直接使用原生的websocket語法建立連接飒硅。
- 初始化時this._config().preurl是寫在配置里的一個url地址,同其他接口地址前綴一樣作谚,但是這里的url地址是http協(xié)議三娩,也就是說一開始是把一個http協(xié)議地址傳給的socket.io,然后socket.io在建立http鏈接之后才升級成的websocket鏈接妹懒。
- on方法雀监,用來監(jiān)聽來自服務(wù)端的事件,once表示監(jiān)聽一次眨唬,還可以取消監(jiān)聽
- emit觸發(fā)事件会前,讓服務(wù)端響應(yīng)。這里的loadAction就表示發(fā)送信息匾竿。
結(jié)語
- 整體寫法有點像觀察者模式瓦宜,服務(wù)端和客戶端都是以監(jiān)聽/觸發(fā)事件的形式完成通信,完全不同于傳統(tǒng)的ajax交互方式岭妖,十分有趣临庇。
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者