天天看點

node.js中ws子產品建立服務端和用戶端

一、

WebSocket

出現的原因

1、Http協定釋出REST API 的不足:

每次請求響應完成之後,伺服器與用戶端之間的連接配接就斷開了,如果用戶端想要繼續擷取伺服器的消息,必須再次向伺服器發起請

求。這顯然無法适應對實時通信有高要求的場景。

2、改善http的不足:Web通信領域出現了一些其他的解決方案,如輪詢、長輪詢、伺服器推送事件、WebSocket

(1)輪詢:就是重複發送新的請求到伺服器。如果伺服器沒有新的資料,就發送适當的訓示并關閉連接配接。然後用戶端等待一段時間

(比如間隔一秒),再發送另一個請求。這種實作方式相對比較簡單,無須做過多的更改。但缺點是輪詢的間隔過長,會導緻使用者不能及

時接收到更新的資料;輪詢時間過短,會導緻查詢請求過多,增加伺服器端的負擔。

(2)長輪詢:用戶端發送一個請求到伺服器,如果伺服器端沒有新的資料,就保持這個連接配接直到有資料。一旦伺服器端有了資料

(消息)給用戶端,它就使用這個連接配接發送資料給用戶端,接着連接配接關閉

(3)伺服器推送事件:Server-Sent Events(SSE),SSE通常重用一個連接配接處理多個消息(事件)。SSE還定義了一個專門的媒體類

型,用于描述一個從服務端發送到用戶端的簡單格式。

(4)WebSocket:提供了一個真正的全雙工連接配接。發起者是一個用戶端,發送一個帶特殊HTTP頭的請求到服務端,通知伺服器。

該方案的優點是屬于html5标準,已經被大多數浏覽器支援,而且是真正的全雙工,性能比較好,其缺點是實作起來比較複雜,需要對ws

協定專門處理。

二、Node使用ws建立WebSocket伺服器

1、Node.js原生API沒有提供對WebSocket的支援,需要安裝第三方包才能使用WebSocket功能

2、ws子產品:是一個用于支援WebSocket用戶端和伺服器的架構。它易于使用,功能強大,且不依賴于其他環境

3、安裝ws:npm install ws

4、建立WebSocket伺服器:

//建立一個WebSocket伺服器,在8080端口啟動
 const WebSocket = require('ws')
 const server = new WebSocket.Server({port:8080})
           

5、WebSocket.Server(options[,callback])方法中options對象所支援的參數

(1)host:綁定伺服器的主機名

(2)port:綁定伺服器的端口号

(3)backlog:挂起連接配接隊列的最大長度

(4)server:預先建立的node.js http/s伺服器

(5)verifyClient:可用于驗證傳入連接配接的函數

(6)handleProtocols:可用于處理WebSocket子協定的函數

(7)path:僅接受與此路徑比對的連接配接

(8)noServer:不啟用伺服器模式

(9)clientTracking:指定是否跟蹤用戶端

(10)perMessageDeflate:啟用/禁用消息壓縮

(11)maxPayload:允許的最大消息大小(以位元組為機關)

三、監聽連接配接:ws通過connection事件來監聽連接配接

server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 })
 //隻要有WebSocket連接配接到該伺服器,就會觸發'connection'事件;req對象可以用來擷取用戶端的資訊,如ip、端口号
 //擷取所有已連接配接的用戶端資訊,則可以使用server.clients資料集
           

四、發送資料:ws通過send()方法來發送資料

/* send(data [,options][,callback])
       data:發送的資料
       options對象:
         (1)compress:指定資料是否需要壓縮。預設為true
         (2)binary:指定資料是否通過二進制傳送。預設是自動檢測
         (3)mask:指定是否應遮罩資料。
         (4)fin:指定資料是否為消息的最後一個片段。預設為true
 */
 server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 })
           

五、接收資料:ws通過message事件來接收資料。當用戶端有消息發送給伺服器時,伺服器就能夠觸發該消息

server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 ​
     ws.on('message',function incoming(message){
         console.log('received: %s from %s',message,clientName)
         server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
         })
     })
 })
           

六、準備的狀态:ws中WebSocket類具有以下4中準備狀态

1、CONNCETION:值為0,表示連接配接還沒有打開

2、OPEN:值為1,表示連接配接已經打開,可以通信了

3、CLOSING:值為2,表示連接配接正在關閉

4、CLOSED:值為2,表示連接配接已經關閉

server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
 })
           

七、關閉WebSocket伺服器:通過監聽close事件關閉伺服器

server.on('close',function close(){
     console.log('disconnected')
 })
           

案例

1、伺服器端:server.js

const WebSocket = require('ws')
 ​
 const server = new WebSocket.Server({port:8080})
 ​
 server.on('open',function open(){
     console.log('connected')
 })
 ​
 server.on('close',function close(){
     console.log('disconnected')
 })
 ​
 server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 ​
     ws.on('message',function incoming(message){
         console.log('received: %s from %s',message,clientName)
         server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
         })
     })
 })
           

2、用戶端:

<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>
 <body>
     <script>
         var socket
         if (!window.WebSocket) {
             window.WebSocket = window.MozWebSocket
         }
         if(window.WebSocket){
             socket = new WebSocket("ws://localhost:8080/ws")
             socket.onmessage = function(event){
                 var ta = document.getElementById('responseTest')
                 ta.value = ta.value + '\n' + event.data
             }
             socket.onopen = function(event) {
                 var ta = document.getElementById('responseTest')
                 ta.value = '連接配接開啟!'
             }
 ​
             socket.onclose = function(event) {
                 var ta = document.getElementById('responseTest')
                 ta.value = '連接配接關閉!'
             }
         }else{
             alert('你的浏覽器不支援WebSocket')
         }
 ​
         function send(message){
             if(!window.WebSocket){
                 return
             }
             if(socket.readyState === WebSocket.OPEN){
                 socket.send(message)
             }else{
                 alert('連接配接沒有開啟')
             }
         }
     </script>
     <form onsubmit="return false">
         <h3>WebSocket 聊天室:</h3>
         <textarea  id="responseTest" style="width: 500px;height: 300px;"></textarea>
         <br>
         <input type="text" name="message" style="width: 300px;" value="Welcome to woniuxy.com">
         <input type="button" value="發送消息" onclick="send(this.form.message.value)">
         <input type="button" value="清空聊天記錄" onclick="javascript:document.getElementById('responseTest').value=''">
     </form>
 </body>
 </html>
           

繼續閱讀