欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

NodeJS落地WebSocket實(shí)踐前端架構(gòu)師破局技術(shù)

 更新時(shí)間:2022年06月22日 10:33:22   作者:楊成功  
這篇文章主要為大家介紹了NodeJS落地WebSocket實(shí)踐前端架構(gòu)師破局技術(shù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

網(wǎng)絡(luò)協(xié)議進(jìn)化

HTTP 協(xié)議是前端最熟悉的網(wǎng)絡(luò)通信協(xié)議。我們通常的打開(kāi)網(wǎng)頁(yè),請(qǐng)求接口,都屬于 HTTP 請(qǐng)求。

HTTP 請(qǐng)求的特點(diǎn)是:請(qǐng)求-> 響應(yīng)??蛻舳税l(fā)起請(qǐng)求,服務(wù)端收到請(qǐng)求后進(jìn)行響應(yīng),一次請(qǐng)求就完成了。也就是說(shuō),HTTP 請(qǐng)求必須由客戶端發(fā)起,服務(wù)端才能被動(dòng)響應(yīng)。

除此之外,發(fā)起 HTTP 請(qǐng)求之前,還需要通過(guò)三次握手建立 TCP 連接。HTTP/1.0 的特點(diǎn)是,每通信一次,都要經(jīng)歷 “三步走” 的過(guò)程 —— TCP 連接 -> HTTP 通信 -> 斷開(kāi) TCP 連接。

這樣的每一次請(qǐng)求都是獨(dú)立的,一次請(qǐng)求完成連接就會(huì)斷開(kāi)。

HTTP1.1 對(duì)請(qǐng)求過(guò)程做了優(yōu)化。TCP 連接建立之后,我們可以進(jìn)行多次 HTTP 通信,等到一個(gè)時(shí)間段無(wú) HTTP 請(qǐng)求發(fā)起 TCP 才會(huì)斷開(kāi)連接,這就是 HTTP/1.1 帶來(lái)的長(zhǎng)連接技術(shù)。

但是即便如此,通信方式依然是客戶端發(fā)起,服務(wù)端響應(yīng),這個(gè)根本邏輯不會(huì)變。

隨著應(yīng)用交互的復(fù)雜,我們發(fā)現(xiàn),有一些場(chǎng)景是必須要實(shí)時(shí)獲取服務(wù)端消息的。

比如即時(shí)聊天,比如消息推送,用戶并不會(huì)主動(dòng)發(fā)起請(qǐng)求,但是當(dāng)服務(wù)器有了新消息,客戶端需要立刻知道并且反饋給用戶。

HTTP 不支持服務(wù)端主動(dòng)推送,但是這些場(chǎng)景又急需解決方案,于是早期出現(xiàn)了輪詢(polling)。輪詢是客戶端定時(shí)向服務(wù)器發(fā)起請(qǐng)求,檢測(cè)服務(wù)端是否有更新,如果有則返回新數(shù)據(jù)。

這種輪詢方式雖然簡(jiǎn)單粗暴,但很顯然有兩個(gè)弊端:

  • 請(qǐng)求消耗太大。客戶端不斷請(qǐng)求,浪費(fèi)流量和服務(wù)器資源,給服務(wù)器造成壓力。
  • 不能保證及時(shí)??蛻舳诵枰胶饧皶r(shí)性和性能,請(qǐng)求間隔必然不能太小,因此會(huì)有延遲。

隨著 HTML5 推出 WebSocket,即時(shí)通訊場(chǎng)景終于迎來(lái)了根本解決方案。WebSocket 是全雙工通信協(xié)議,當(dāng)客戶端與服務(wù)端建立連接之后,雙方可以互相發(fā)送數(shù)據(jù),這樣的話就不需要客戶端通過(guò)輪詢這種低效的方式獲取數(shù)據(jù),服務(wù)端有新消息直接推送給客戶端即可。

傳統(tǒng) HTTP 連接方式如下:

## 普通連接
http://localhost:80/test
## 安全連接
https://localhost:80/test

WebSocket 是另一種協(xié)議,連接方式如下:

## 普通連接
ws://localhost:80/test
## 安全連接
wss://localhost:80/test

但是 WebSocket 也不是完全脫離 HTTP 的,若要建立 WebSocket 連接,則必須要客戶端主動(dòng)發(fā)起一個(gè)建立連接的 HTTP 請(qǐng)求,連接成功之后客戶端與服務(wù)端才能進(jìn)行雙向通信。

Socket.IO?

提起用 Node.js 實(shí)現(xiàn) WebSocket,大家一定會(huì)想到一個(gè)庫(kù):Socket.IO

沒(méi)錯(cuò),Socket.IO 是目前 Node.js 在生產(chǎn)環(huán)境中開(kāi)發(fā) WebSocket 應(yīng)用最好的選擇。它功能強(qiáng)大,高性能,低延遲,并且可以一步集成到 express 框架中。

但是也許你不清楚,Socket.IO 并不是一個(gè)純粹的 WebSocket 框架。它是將 Websocket 和輪詢機(jī)制以及其它的實(shí)時(shí)通信方式封裝成了通用的接口,以實(shí)現(xiàn)更高效的雙向通信。

嚴(yán)格來(lái)說(shuō),Websocket 只是 Socket.IO 的一部分。

也許你會(huì)問(wèn):既然 Socket.IO 在 WebSocket 的基礎(chǔ)上做了那么多的優(yōu)化,并且非常成熟,那為什么還要搭一個(gè)原生 WebSocket 服務(wù)?

首先,Socket.IO 不能通過(guò)原生的 ws 協(xié)議連接。比如你在瀏覽器試圖通過(guò) ws://localhost:8080/test-socket 這種方式連接 Socket.IO 服務(wù),是連接不上的。因?yàn)?Socket.IO 的服務(wù)端必須通過(guò) Socket.IO 的客戶端連接,不支持默認(rèn)的 WebSocket 方式連接。

其次,Socket.IO 封裝程度非常高,使用它可能不利于你了解 WebSocket 建立連接的原理。

因此,我們本篇就用 Node.js 中基礎(chǔ)的 ws 模塊,從頭開(kāi)始實(shí)現(xiàn)一個(gè)原生的 WebSocket 服務(wù),并且在前端用 ws 協(xié)議直接連接,體驗(yàn)一把雙向通信的感覺(jué)!

ws 模塊實(shí)現(xiàn)

ws 是 Node.js 下一個(gè)簡(jiǎn)單快速,并且定制程度極高的 WebSocket 實(shí)現(xiàn)方案,同時(shí)包含了服務(wù)端和客戶端。

ws 搭建起來(lái)的服務(wù)端,瀏覽器可以通過(guò)原生 WebSocket 構(gòu)造函數(shù)直接連接,非常便捷。ws 客戶端則是模擬瀏覽器的 WebSocket 構(gòu)造函數(shù),用于連接其他 WebSocket 服務(wù)器進(jìn)行通信。

注意一點(diǎn):ws 只能在 Node.js 環(huán)境中使用,瀏覽器中不可用,瀏覽器請(qǐng)直接使用原生 WebSocket 構(gòu)造函數(shù)。

下面開(kāi)始接入,第一步,安裝 ws:

$ npm install ws

安裝好后,我們先搭建一個(gè) ws 服務(wù)端。

服務(wù)端

搭建 websocket 服務(wù)器需要用 WebSocketServer 構(gòu)造函數(shù)。

const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({
  port: 8080
})
wss.on('connection', (ws, req) => {
  console.log('客戶端已連接:', req.socket.remoteAddress)
  ws.on('message', data => {
    console.log('收到客戶端發(fā)送的消息:', data)
  })
  ws.send('我是服務(wù)端') // 向當(dāng)前客戶端發(fā)送消息
})

把這段代碼寫進(jìn) ws-server.js 然后運(yùn)行:

$ node ws-server.js

客戶端

上一步建好了 WebSocket 服務(wù)器,現(xiàn)在我們?cè)谇岸诉B接并監(jiān)聽(tīng)消息:

var ws = new WebSocket('ws://localhost:8080')
ws.onopen = function(mevt) {
  console.log('客戶端已連接')
}
ws.onmessage = function(mevt) {
  console.log('客戶端收到消息: ' + evt.data)
  ws.close()
}
ws.onclose = function(mevt) {
  console.log('連接關(guān)閉')
}

將代碼寫入 wsc.html 然后用瀏覽器打開(kāi),看到打印如下:

可以看到,瀏覽器連接成功后,收到服務(wù)端主動(dòng)推送過(guò)來(lái)的消息,然后瀏覽器可以主動(dòng)關(guān)閉連接。

Node.js 環(huán)境下我們看 ws 模塊如何發(fā)起連接:

const WebSocket = require('ws')
var ws = new WebSocket('ws://localhost:8080')
ws.on('open', () => {
  console.log('客戶端已連接')
})
ws.on('message', data => {
  console.log('客戶端收到消息: ' + data)
  ws.close()
})
ws.on('close', () => {
  console.log('連接關(guān)閉')
})

代碼與瀏覽器的邏輯一摸一樣,只是寫法稍有些不同,注意區(qū)別。

需要特殊說(shuō)明的一點(diǎn),瀏覽器端監(jiān)聽(tīng) message 事件的回調(diào)函數(shù),參數(shù)是一個(gè) MessageEvent 的實(shí)例對(duì)象,服務(wù)端發(fā)來(lái)的實(shí)際數(shù)據(jù)需要通過(guò) mevt.data 獲取。

而在 ws 客戶端,這個(gè)參數(shù)就是服務(wù)端的實(shí)際數(shù)據(jù),直接獲取即可。

Express 集成

ws 模塊一般不會(huì)單獨(dú)使用,更優(yōu)的方案是集成到現(xiàn)有的框架中。這節(jié)我們將 ws 模塊集成到 Express 框架。

集成到 Express 框架的優(yōu)點(diǎn)是,我們不需要單獨(dú)監(jiān)聽(tīng)一個(gè)端口,使用框架啟動(dòng)的端口即可,并且我們還可以指定訪問(wèn)到某個(gè)路由,才發(fā)起 WebSocket 連接。

幸運(yùn)的是這一切不需要手動(dòng)實(shí)現(xiàn),express-ws 模塊已經(jīng)幫我們做好了大部分的集成工作。

首先安裝,然后在入口文件引入:

var expressWs = require('express-ws')(app)

和 Express 的 Router 一樣,express-ws 也支持注冊(cè)全局路由和局部路由。

先看全局路由,通過(guò) [host]/test-ws 連接:

app.ws('/test-ws', (ws, req) => {
  ws.on('message', msg => {
    ws.send(msg)
  })
})

局部路由則是注冊(cè)在一個(gè)路由組下面的子路由。配置一個(gè)名為 websocket 的路由組并指向 websocket.js 文件,代碼如下:

// websocket.js
var router = express.Router()
router.ws('/test-ws', (ws, req) => {
  ws.on('message', msg => {
    ws.send(msg)
  })
})
module.exports = router

連接 [host]/websocket/test-ws 就可以訪問(wèn)到這個(gè)子路由。

路由組的作用是定義一個(gè) websocket 連接組,不同需求連接這個(gè)組下的不同子路由。比如可以將 單聊 和 群聊 設(shè)置為兩個(gè)子路由,分別處理各自的連接通信邏輯。

完整代碼如下:

var express = require('express')
var app = express()
var wsServer = require('express-ws')(app)
var webSocket = require('./websocket.js')
app.ws('/test-ws', (ws, req) => {
  ws.on('message', msg => {
    ws.send(msg)
  })
})
app.use('/websocket', webSocket)
app.listen(3000)

實(shí)際開(kāi)發(fā)中獲取常用信息的小方法:

// 客戶端的IP地址
req.socket.remoteAddress
// 連接參數(shù)
req.query

WebSocket 實(shí)例

WebSocket 實(shí)例是指客戶端連接對(duì)象,以及服務(wù)端連接的第一個(gè)參數(shù)。

var ws = new WebSocket('ws://localhost:8080')
app.ws('/test-ws', (ws, req) => {}

代碼中的 ws 就是 WebSocket 實(shí)例,表示建立的連接。

瀏覽器

瀏覽器的 ws 對(duì)象中包含的信息如下:

{
  binaryType: 'blob'
  bufferedAmount: 0
  extensions: ''
  onclose: null
  onerror: null
  onmessage: null
  onopen: null
  protocol: ''
  readyState: 3
  url: 'ws://localhost:8080/'
}

首先非常關(guān)鍵的是四個(gè)監(jiān)聽(tīng)屬性,用于定義函數(shù):

  • onopen:連接建立后的函數(shù)
  • onmessage:收到服務(wù)端推送消息的函數(shù)
  • onclose:連接關(guān)閉的函數(shù)
  • onerror:連接異常的函數(shù)

其中最常用的是 onmessage 屬性,賦值為一個(gè)函數(shù)來(lái)監(jiān)聽(tīng)服務(wù)端消息:

ws.onmessage = mevt => {
  console.log('消息:', mevt.data)
}

還有一個(gè)關(guān)鍵屬性是 readyState,表示連接狀態(tài),值為一個(gè)數(shù)字。并且每個(gè)值都可以用常量表示,對(duì)應(yīng)關(guān)系和含義如下:

  • 0: 常量 WebSocket.CONNECTING,表示正在連接
  • 1: 常量 WebSocket.OPEN,表示已連接
  • 2: 常量 WebSocket.CLOSING,表示正在關(guān)閉
  • 3: 常量 WebSocket.CLOSED,表示已關(guān)閉

當(dāng)然最重要的還有 send 方法用于發(fā)送信息,向服務(wù)端發(fā)送數(shù)據(jù):

ws.send('要發(fā)送的信息')

服務(wù)端

服務(wù)端的 ws 對(duì)象表示當(dāng)前發(fā)起連接的一個(gè)客戶端,基本屬性與瀏覽器大致相同。

比如上面客戶端的四個(gè)監(jiān)聽(tīng)屬性,readyState 屬性,以及 send 方法都是一致的。不過(guò)因?yàn)榉?wù)端是 Node.js 實(shí)現(xiàn),因此會(huì)有更豐富的支持。

比如下面兩種監(jiān)聽(tīng)事件的寫法效果是一樣的:

// Node.js 環(huán)境
ws.onmessage = str => {
  console.log('消息:', str)
}
ws.on('message', str => {
  console.log('消息:', mevt.data)
})

詳細(xì)的屬性和介紹可以查閱官方文檔

消息廣播

WebSocket 服務(wù)器不會(huì)只有一個(gè)客戶端連接,消息廣播的意思就是把信息發(fā)給所有已連接的客戶端,像一個(gè)大喇叭一樣,所有人都聽(tīng)得到,經(jīng)典場(chǎng)景就是熱點(diǎn)推送。

那么廣播之前,就必須要解決一個(gè)問(wèn)題,如何獲取當(dāng)前已連接(在線)的客戶端?

其實(shí) ws 模塊提供了快捷的獲取方法:

var wss = new WebSocketServer({ port: 8080 })
// 獲取所有已連接客戶端
wss.clients

方便吧。再看 express-ws 怎么獲?。?/p>

var wsServer = expressWebSocket(app)
var wss = wsServer.getWss()
// 獲取所有已連接客戶端
wss.clients

拿到 wss.clients 后,我們看看它到底是什么樣子。經(jīng)過(guò)打印,發(fā)現(xiàn)它的數(shù)據(jù)結(jié)構(gòu)比想象到還要簡(jiǎn)單,就是由所有在線客戶端的 WebSocket 實(shí)例組成的一個(gè) Set 集合。

那么,獲取當(dāng)前在線客戶端的數(shù)量:

wss.clients.size

簡(jiǎn)單粗暴的實(shí)現(xiàn)廣播:

wss.clients.forEach(client => {
  if (client.readyState === 1) {
    client.send('廣播數(shù)據(jù)')
  }
})

這是非常簡(jiǎn)單,基礎(chǔ)的實(shí)現(xiàn)方式。試想一下如果此刻在線客戶有 10000 個(gè),那么這個(gè)循環(huán)多半會(huì)卡死吧。因此才會(huì)有像 socket.io 這樣的庫(kù),對(duì)基礎(chǔ)功能做了大量?jī)?yōu)化和封裝,提高并發(fā)性能。

上面的廣播屬于全局廣播,就是將消息發(fā)給所有人。然而還有另一種場(chǎng)景,比如一個(gè) 5 人的群聊小組聊天,這時(shí)的廣播只是給這 5 人小團(tuán)體發(fā)消息,因此這也叫 局部廣播。

局部廣播的實(shí)現(xiàn)要復(fù)雜一些,一般會(huì)揉合具體的業(yè)務(wù)場(chǎng)景。這就需要我們?cè)诳蛻舳诉B接時(shí),對(duì)客戶端數(shù)據(jù)做持久化處理了。比如用 Redis 存儲(chǔ)在線客戶端的狀態(tài)和數(shù)據(jù),這樣檢索分類更快,效率更高。

局部廣播實(shí)現(xiàn),那一對(duì)一私聊就更容易了。找到兩個(gè)客戶端對(duì)應(yīng)的 WebSocket 實(shí)例互發(fā)消息就行。

安全與認(rèn)證

前面搭建好的 WebSocket 服務(wù)器,默認(rèn)任何客戶端都可以連接,這在生產(chǎn)環(huán)境肯定是不行的。我們要對(duì) WebSocket 服務(wù)器做安全保障,主要是從兩個(gè)方面入手:

  • Token 連接認(rèn)證
  • wss 支持

下面說(shuō)一說(shuō)我的實(shí)現(xiàn)思路。

Token 連接認(rèn)證

HTTP 請(qǐng)求接口我們一般會(huì)做 JWT 認(rèn)證,在請(qǐng)求頭中帶一個(gè)指定 Header,將一個(gè) token 字符串傳過(guò)去,后端會(huì)拿這個(gè) token 做校驗(yàn),校驗(yàn)失敗則返回 401 錯(cuò)誤阻止請(qǐng)求。

我們上面說(shuō)過(guò),WebSocket 建立連接的第一步是客戶端發(fā)起一個(gè) HTTP 的連接請(qǐng)求,那么我們?cè)谶@個(gè) HTTP 請(qǐng)求上做驗(yàn)證,如果驗(yàn)證失敗,則中段 WebSocket 的連接創(chuàng)建,不就可以了?

順著這個(gè)思路,我們來(lái)改造一下服務(wù)端代碼。

因?yàn)橐?HTTP 層做校驗(yàn),所以用 http 模塊創(chuàng)建服務(wù)器,關(guān)掉 WebSocket 服務(wù)的端口。

var server = http.createServer()
var wss = new WebSocketServer({ noServer: true })
server.listen(8080)

當(dāng)客戶端通過(guò) ws:// 連接服務(wù)端時(shí),服務(wù)端會(huì)進(jìn)行協(xié)議升級(jí),也就是將 http 協(xié)議升級(jí)成 websocket 協(xié)議,此時(shí)會(huì)觸發(fā) upgrade 事件:

server.on('upgrade', (request, socket) => {
  // 用 request 獲取參數(shù)做驗(yàn)證
  // 1. 驗(yàn)證不通過(guò)判斷
  if ('驗(yàn)證失敗') {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
    socket.destroy()
    return
  }
  // 2. 驗(yàn)證通過(guò),繼續(xù)建立連接
  wss.handleUpgrade(request, socket, _, ws => {
    wss.emit('connection', ws, request)
  })
})
// 3. 監(jiān)聽(tīng)連接
wss.on('connection', (ws, request) => {
  console.log('客戶端已連接')
  ws.send('服務(wù)端信息')
})

這樣服務(wù)端認(rèn)證添加完畢,具體的認(rèn)證方法結(jié)合客戶端的傳參方式來(lái)定。

WebSocket 客戶端連接不支持自定義 Header,因此不能用 JWT 的方案,可用方案有兩種:

  • Basic Auth
  • Quary 傳參

Basic Auth 認(rèn)證簡(jiǎn)單說(shuō)就是賬號(hào)+密碼認(rèn)證,而且賬號(hào)密碼是帶在 URL 里的。

假設(shè)我有賬號(hào)是 ruims,密碼是 123456,那么客戶端連接是這樣:

var ws = new WebSocket('ws://ruims:123456@localhost:8080')

那么服務(wù)端就會(huì)收到這樣一個(gè)請(qǐng)求頭:

wss.on('connection', (ws, req) => {
  if(req.headers['authorization']) {
    let auth = req.headers['authorization']
    console.log(auth)
    // 打印的值:Basic cnVpbXM6MTIzNDU2
  }
}

其中 cnVpbXM6MTIzNDU2 就是 ruims:123456 的 base64 編碼,服務(wù)端可以獲取到這個(gè)編碼來(lái)做認(rèn)證。

Quary 傳參比較簡(jiǎn)單,就是普通的 URL 傳參,可以帶一個(gè)短一點(diǎn)的加密字符串過(guò)去,服務(wù)端獲取到該字符串然后做認(rèn)證:

var ws = new WebSocket('ws://localhost:8080?token=cnVpbXM6MTIzNDU2')

服務(wù)端獲取參數(shù):

wss.on('connection', (ws, req) => {
  console.log(req.query.token)
}

wss 支持

WebSocket 客戶端使用 ws:// 協(xié)議連接,那 wss 是什么意思?

其實(shí)非常簡(jiǎn)單,和 https 原理一摸一樣。

https 表示安全的 http 協(xié)議,組成是 HTTP + SSL

wss 則表示安全的 ws 協(xié)議,組成是 WS + SSL

那為什么一定要用 wss 呢?除了安全性,還有一個(gè)關(guān)鍵原因是:如果你的 web 應(yīng)用是 https 協(xié)議,你在當(dāng)前應(yīng)用中使用 WebSocket 就必須是 wss 協(xié)議,否則瀏覽器拒絕連接。

配置 wss 直接在 https 配置中加一個(gè) location 即可,直接上 nginx 配置:

location /websocket {
  proxy_pass http://127.0.0.1:8080;
  proxy_redirect off;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection upgrade;
}

然后客戶端連接就變成了這樣:

var ws = new WebSocket('wss://[host]/websocket')

BFF 應(yīng)用

BFF 或許你聽(tīng)說(shuō)過(guò),全稱是 Backend For Frontend,意思是為前端服務(wù)的后端,在實(shí)際應(yīng)用架構(gòu)中屬于前端和后端的一個(gè) 中間層。

這個(gè)中間層一般是由 Node.js 實(shí)現(xiàn),那么它有什么作用呢?

眾所周知,現(xiàn)在后端的主流架構(gòu)是微服務(wù),微服務(wù)情況下 API 會(huì)劃分的非常細(xì),商品服務(wù)就是商品服務(wù),通知服務(wù)就是通知服務(wù)。當(dāng)你想在商品上架時(shí)給用戶發(fā)一個(gè)通知,可能至少需要調(diào)兩個(gè)接口。

這樣的話對(duì)前端其實(shí)是不友好的,于是后來(lái)出現(xiàn)了 BFF 中間層,相當(dāng)于一個(gè)后端請(qǐng)求的中間代理站,前端可以直接請(qǐng)求 BFF 的接口,然后 BFF 再向后端接口請(qǐng)求,將需要的數(shù)據(jù)組合起來(lái),一次返回前端。

那我們?cè)谏厦嬷v的一大堆 WebSocket 的知識(shí),在 BFF 層如何應(yīng)用呢?

我想到的應(yīng)用場(chǎng)景至少有 4 個(gè):

  • 查看當(dāng)前在線人數(shù),在線用戶信息
  • 登錄新設(shè)備,其他設(shè)備退出登錄
  • 檢測(cè)網(wǎng)絡(luò)連接/斷開(kāi)
  • 站內(nèi)消息,小圓點(diǎn)提示

這些功能以前是在后端實(shí)現(xiàn)的,并且會(huì)與其他業(yè)務(wù)功能耦合。現(xiàn)在有了 BFF,那么 WebSocket 完全可以在這一層實(shí)現(xiàn),讓后端可以專注核心數(shù)據(jù)邏輯。

由此可見(jiàn),掌握了 WebSocket 在 Node.js 中的實(shí)踐應(yīng)用,作為前端的我們可以破除內(nèi)卷,在另一個(gè)領(lǐng)域繼續(xù)發(fā)揮價(jià)值,豈不美哉?

源碼地址:https://github.com/ruidoc/blog-codes

以上就是NodeJS落地WebSocket實(shí)踐前端架構(gòu)師破局技術(shù)的詳細(xì)內(nèi)容,更多關(guān)于前端架構(gòu)NodeJS WebSocket的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論