成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

用 Node 實(shí)現(xiàn)簡單 WebSocket 協(xié)議

chavesgu / 3402人閱讀

摘要:用實(shí)現(xiàn)簡單協(xié)議從瀏覽器說起瀏覽器提供的非常簡潔。創(chuàng)建連接連接建立時(shí)的回調(diào)收到消息時(shí)的回調(diào)連接出錯(cuò)時(shí)的回調(diào)連接終止時(shí)的回調(diào)發(fā)送消息告訴我們也就是說,在創(chuàng)建對(duì)象時(shí),瀏覽器嘗試與服務(wù)端建立連接發(fā)送請(qǐng)求建立個(gè)服務(wù)端一旦收到數(shù)據(jù),就會(huì)觸發(fā)。

用 Node 實(shí)現(xiàn)簡單 WebSocket 協(xié)議 從瀏覽器 WebSocket API 說起

瀏覽器提供的 WebSocket API 非常簡潔。

let ws = new WebSocket("ws://localhost:8124") // 創(chuàng)建連接
ws.onopen = function(e){...}  // 連接建立時(shí)的回調(diào)
ws.onmessage = function(e){...}  // 收到消息時(shí)的回調(diào)
ws.onerror = function(e){...}  // 連接出錯(cuò)時(shí)的回調(diào)
ws.onclose = function(e){...}  // 連接終止時(shí)的回調(diào)
ws.send("Hello Server!") // 發(fā)送消息

MDN告訴我們

In order to communicate using the WebSocket protocol, you need to create a WebSocket object; this will automatically attempt to open the connection to the server.

也就是說,在 let ws = new WebSocket("ws://localhost:8124") 創(chuàng)建 WebSocket 對(duì)象時(shí),瀏覽器嘗試與服務(wù)端建立連接(發(fā)送請(qǐng)求)

建立個(gè)服務(wù)端
const net = require("net")
const server = net.createServer(s => {
  s.on("data", d => {
    console.log(d.toString())
  })
})
server.listen(8124, () => {
  console.log("listening on 8124...")
})

一旦收到數(shù)據(jù),就會(huì)觸發(fā) data。

啟動(dòng)服務(wù)端,監(jiān)聽8124端口;在瀏覽器中打開控制臺(tái),輸入let ws = new WebSocket("ws://localhost:8124");得到結(jié)果如下

通過這次請(qǐng)求,瀏覽器告訴服務(wù)端,要升級(jí)協(xié)議為 websocket

服務(wù)端收到這個(gè)請(qǐng)求后,向?yàn)g覽器發(fā)出響應(yīng),這個(gè)過程就叫做握手(handshaking)

服務(wù)端向?yàn)g覽器發(fā)出同意升級(jí)協(xié)議的響應(yīng)后會(huì)觸發(fā)瀏覽器端 websocket.onopen 的回調(diào)

服務(wù)端的響應(yīng)

Obtain the value of Sec-WebSocket-Key request header without any leading and trailing whitespace

Link it with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

Compute SHA-1 and Base64 code of it

Write it back as value of Sec-WebSocket-Accept response header as part of a HTTP response.

根據(jù)MDN的指示,修改服務(wù)端代碼

const net = require("net")
const crypto = require("crypto")
const server = net.createServer(s => {
  s.on("data", d => {
    let req = d.toString() 
    // Obtain the value of Sec-WebSocket-Key request header without any leading and trailing whitespace
    let secWebsocketKey = /Sec-WebSocket-Key:s(.*)/.exec(req)[1]
    // Link it with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    let key = secWebsocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    // Compute SHA-1 and Base64 code of it
    let secWebSocketAccept = crypto.createHash("sha1").update(key).digest("base64")
    // Write it back as value of Sec-WebSocket-Accept response header as part of a HTTP response.
    let res = "HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: " + secWebSocketAccept + "

"
    s.write(res)
  })
})
server.listen(8124, () => {
  console.log("listening on 8124...")
})

同時(shí)為了方便測(cè)試,寫個(gè)html文件





  
  
  
  socketClient



  socketClient
  


啟動(dòng)服務(wù)端,用瀏覽器打開html文件

至此之后,瀏覽器和服務(wù)端之間就可以更加愉快地聊天了,之前的服務(wù)端很矜持,瀏覽器問一句,服務(wù)端答一句;而握手之后,服務(wù)端也會(huì)主動(dòng)向?yàn)g覽器發(fā)送消息,這樣就會(huì)觸發(fā)瀏覽器端 websocket.onmessage 的回調(diào)

服務(wù)端主動(dòng)發(fā)送消息

修改服務(wù)端代碼

...
  s.on("data", d => {
    let req = d.toString()
    console.log(req)
    // Obtain the value of Sec-WebSocket-Key request header without any leading and trailing whitespace
    let secWebsocketKey = /Sec-WebSocket-Key:s(.*)/.exec(req)[1]
    // Link it with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    let key = secWebsocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    // Compute SHA-1 and Base64 code of it
    let secWebSocketAccept = crypto.createHash("sha1").update(key).digest("base64")
    // Write it back as value of Sec-WebSocket-Accept response header as part of a HTTP response.
    let res = "HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: " + secWebSocketAccept + "

"
    console.log(res)
    s.write(res)
    // 服務(wù)端主動(dòng)發(fā)消息 Hi, Client! l"m Server.
    let dataBuffer = new Buffer(`Hi, Client! l"m Server.`)
    let payload_len = dataBuffer.length
    let assistData = []
    assistData.push(129)
    assistData.push(payload_len)
    let assistBuffer = new Buffer(assistData)
    let message = Buffer.concat([assistBuffer, dataBuffer])
    console.log(message)
    s.write(message)
  })
})
...

代碼中

    // 服務(wù)端主動(dòng)發(fā)消息 Hi, Client! l"m Server.
    let dataBuffer = new Buffer(`Hi, Client! l"m Server.`)
    let payload_len = dataBuffer.length
    let assistData = []
    assistData.push(129)
    assistData.push(payload_len)
    let assistBuffer = new Buffer(assistData)
    let message = Buffer.concat([assistBuffer, dataBuffer])
    console.log(message)
    s.write(message)

實(shí)際上是將數(shù)據(jù)編碼成數(shù)據(jù)幀的過程,其具體細(xì)節(jié)稍后再說。

修改html

websocket.send()

瀏覽器發(fā)送消息很簡單

修改html

修改服務(wù)端代碼

const net = require("net")
const crypto = require("crypto")
const server = net.createServer(s => {
  s.handshaking = false
  s.on("data", d => {
    if (!s.handshaking) {
      let req = d.toString()
      console.log(req)
      // Obtain the value of Sec-WebSocket-Key request header without any leading and trailing whitespace
      let secWebsocketKey = /Sec-WebSocket-Key:s(.*)/.exec(req)[1]
      // Link it with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
      let key = secWebsocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
      // Compute SHA-1 and Base64 code of it
      let secWebSocketAccept = crypto.createHash("sha1").update(key).digest("base64")
      // Write it back as value of Sec-WebSocket-Accept response header as part of a HTTP response.
      let res = "HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: " + secWebSocketAccept + "

"
      console.log(res)
      s.write(res)
      // 服務(wù)端主動(dòng)發(fā)消息 Hi, Client! l"m Server.
      let dataBuffer = new Buffer(`Hi, Client! l"m Server.`)
      let payload_len = dataBuffer.length
      let assistData = []
      assistData.push(129)
      assistData.push(payload_len)
      let assistBuffer = new Buffer(assistData)
      let message = Buffer.concat([assistBuffer, dataBuffer])
      console.log(message)
      s.write(message)
      s.handshaking = true
    } else {
      //解析瀏覽器發(fā)送消息
      let fin = d[0] >> 7
      let opcode = d[0] & parseInt(1111, 2) // 1 表示文本數(shù)據(jù)幀
      let mask = d[1] >> 7 // 標(biāo)示是否進(jìn)行掩碼處理,客戶端發(fā)送給服務(wù)端時(shí)為1,服務(wù)端發(fā)送給客戶端時(shí)為0
      let payload_len = d[1] & parseInt(1111111, 2) // 這里假設(shè)發(fā)送的數(shù)據(jù)長度小于 125
      let masking_key = d.slice(2, 6)
      let payload_data = new Buffer(payload_len)
      for (let i = 0; i < payload_len; i++) {
        let j = i % 4
        payload_data[i] = d[6 + i] ^ masking_key[j]
      }
      console.log(payload_data.toString())
    }
  })
})
server.listen(8124, () => {
  console.log("listening on 8124...")
})

編碼解碼數(shù)據(jù)幀

解碼Hi, Server! l"m Client.

let fin = d[0] >> 7
      let opcode = d[0] & parseInt(1111, 2) // 1 表示文本數(shù)據(jù)幀
      let mask = d[1] >> 7 // 標(biāo)示是否進(jìn)行掩碼處理,客戶端發(fā)送給服務(wù)端時(shí)為1,服務(wù)端發(fā)送給客戶端時(shí)為0
      let payload_len = d[1] & parseInt(1111111, 2) // 這里假設(shè)發(fā)送的數(shù)據(jù)長度小于 125
      let masking_key = d.slice(2, 6)
      let payload_data = new Buffer(payload_len)
      for (let i = 0; i < payload_len; i++) {
        let j = i % 4
        payload_data[i] = d[6 + i] ^ masking_key[j]
      }
      console.log(payload_data.toString())

編碼Hi, Client! l"m Server.

let dataBuffer = new Buffer(`Hi, Client! l"m Server.`)
      let payload_len = dataBuffer.length
      let assistData = []
      assistData.push(129)
      assistData.push(payload_len)
      let assistBuffer = new Buffer(assistData)
      let message = Buffer.concat([assistBuffer, dataBuffer])
      console.log(message)

解碼編碼依據(jù)Base Framing Protocol

主要用到 nodeAPI 中 Buffer 的模塊以及位運(yùn)算

其實(shí)我主要看的是《深入淺出NodeJS》第七章websocket的那部分

小結(jié)

握手之后才可以通信

從請(qǐng)求頭獲取websocketKey

設(shè)置響應(yīng)頭

通信

編碼信息

解碼信息

位運(yùn)算/Buffer/正則

let socketArr = []
const server = net.createServer(s => {
  s.handShaking === false
  s.name = `Client_${socketArr.length}`
  socketArr.push(s)
  s.on("data", d => {})
  ...
})

將每個(gè)socket對(duì)象標(biāo)記并保存,從而實(shí)現(xiàn)對(duì)象之間的通信

參考資料

WebSocket

Writing WebSocket client applicaitons

Writing a WebSocket server in Java

《深入淺出NodeJS》第六章、第七章部分

The WebSocket Protocol

用Node實(shí)現(xiàn)WebSocket協(xié)議

基于node實(shí)現(xiàn)websocket協(xié)議

nodejs實(shí)現(xiàn)websocket的數(shù)據(jù)接收發(fā)送

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90464.html

相關(guān)文章

  • Node.js - 200 多行代碼實(shí)現(xiàn) Websocket 協(xié)議

    摘要:預(yù)備工作序最近正在研究相關(guān)的知識(shí),想著如何能自己實(shí)現(xiàn)協(xié)議。監(jiān)聽事件就是協(xié)議的抽象,直接在上面監(jiān)聽已有的事件和事件這兩個(gè)事件。表示當(dāng)前數(shù)據(jù)幀為消息的最后一個(gè)數(shù)據(jù)幀,此時(shí)接收方已經(jīng)收到完整的消息,可以對(duì)消息進(jìn)行處理。 A、預(yù)備工作 1、序 最近正在研究 Websocket 相關(guān)的知識(shí),想著如何能自己實(shí)現(xiàn) Websocket 協(xié)議。到網(wǎng)上搜羅了一番資料后用 Node.js 實(shí)現(xiàn)該協(xié)議,倒也沒...

    張巨偉 評(píng)論0 收藏0
  • 淺談 WebSocket

    摘要:接口用于接收服務(wù)器發(fā)送的事件。因此,是目前來說最佳的選擇。最大特點(diǎn)就是,服務(wù)器可以主動(dòng)向客戶端推送消息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是一種不受限的全雙工通信。若是,則交給的回調(diào)函數(shù)處理,否則,還是走正常的回調(diào)的路子。 使用 WebSocket 的理由 傳統(tǒng)的http協(xié)議有一個(gè)根本性的缺陷,那就是請(qǐng)求只能由客戶端向服務(wù)器發(fā)起,服務(wù)器接收到請(qǐng)求后再進(jìn)行響應(yīng),把數(shù)據(jù)返回給客戶端。也就是...

    Jensen 評(píng)論0 收藏0
  • WebSockets實(shí)戰(zhàn):在 Node 和 React 之間進(jìn)行實(shí)時(shí)通信

    摘要:服務(wù)器推遲響應(yīng),直到發(fā)生更改更新或超時(shí)。旨在取代現(xiàn)有的雙向通信技術(shù)。只要我們對(duì)套接字事件和有了充分的了解,理解和實(shí)現(xiàn)就非常簡單。 翻譯:瘋狂的技術(shù)宅 原文:blog.logrocket.com/websockets-… showImg(https://user-gold-cdn.xitu.io/2019/5/21/16ada008fc1f81d7); Web 為了支持客戶端和服務(wù)器之間的全...

    DevWiki 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<