websocket 淺析

概述

WebSocket 是 HTML5 開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。
在 WebSocket API 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。php

在JS中建立WebSocket後,會有一個HTTP請求發向瀏覽器以發起請求。在取得服務器響應後,創建的鏈接會使用HTTP升級將HTTP協議轉換爲WebSocket協議。node

ajax輪詢和 websocket 鏈接示意圖
image.pngweb

握手鍊接

WebSocket是應用層協議,是TCP/IP協議的子集,經過HTTP/1.1協議的101狀態碼進行握手。
WebSocket協議的創建須要先借助HTTP協議,在服務器返回101狀態碼以後,就能夠進行websocket全雙工雙向通訊了。ajax

web端的請求頭部:
web端websocket請求連接頭部api

1. 首先瀏覽器發送http 的get 請求
客戶端端口 60992 服務端端口 8181
image.png瀏覽器

2. 緊接着服務端返回 101
image.png服務器

字段說明
  • Connection必須設置Upgrade。
  • Upgrade字段必須設置Websocket。
  • Sec-WebSocket-Key是隨機的字符串,服務器端會用這些數據來構造出一個SHA-1的信息摘要。把「Sec-WebSocket-Key」加上一個特殊字符串「258EAFA5-E914-47DA-95CA-C5AB0DC85B11」,而後計算SHA-1摘要,以後進行BASE-64編碼,將結果作爲「Sec-WebSocket-Accept」頭的值,返回給客戶端。如此操做,能夠儘可能避免普通HTTP請求被誤認爲Websocket協議。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,以前草案的版本均應當棄用。
  • Origin字段是可選的,一般用來表示在瀏覽器中發起此Websocket鏈接所在的頁面,相似於Referer。可是,與Referer不一樣的是,Origin只包含了協議和主機名稱。
  • 其餘一些定義在HTTP協議中的字段,如Cookie等,也能夠在Websocket中使用。

傳輸消息

客戶端向服務端發送消息 nick 999websocket

客戶端代碼:
簡單的建立websocket 鏈接,鏈接成功後發送一條消息, 內容爲 nick 999socket

var nick = getQueryString('nick')||'江芊'
    var url = 'ws://localhost:8181/'
      var Socket = new WebSocket(url);
      Socket.onopen = function(evt) {
        console.log('open', evt)
        Socket.send('nick ' + nick)
      }
      Socket.onclose = function(evt){
        console.log('close', evt)
      }
      Socket.onmessage = function(evt){
        console.log('message', evt)
        var data = JSON.parse(evt.data)
        console.log(data)
        updataMsg(data)
      }
      Socket.onerror = function(evt){
        console.log('error', evt)
      }

客戶端向服務端發送消息 nick 999

服務端向客戶端發送消息 {"type":"nick","message":"346226260347224250346210267:999"}
服務端代碼:
經過node 起的websocket服務,端口爲8181,在接收到客戶端的消息後,發送一條消息給客戶端: {"type":"nick","message":"346226260347224250346210267:999"}tcp

var WebSocketServer = require('ws').Server;

var wss = new WebSocketServer({ port: 8181 });
var clients = []
var id = 0
wss.on('connection', function (ws) {
  console.log('client connected');
  clients.push({
    id: ++id,
    ws: ws
  })

  ws.on('message', function (message) {
    console.log('message', message);
    if(message.indexOf('nick') === 0) {
      let nickname_array = message.split(' ');
      if(nickname_array.length >= 2) {
        broadcastSend("nick", '新用戶:'+nickname_array[1]);
      }
    } else if(message.indexOf('PRIVMSG') === 0) {
        var msglist = message.split(' ')
        broadcastSend("message", msglist[1]);
    }
  });
  function broadcastSend(type, message) {
    clients.forEach(function(v, i) {
        if(v.ws.readyState === ws.OPEN) {
            v.ws.send(JSON.stringify({
                "type": type,
                "message": message
            }));
        }
    })
  }
});

image.png

能夠看出在websocket發送消息的鏈接傳輸中,客戶端端口仍是爲60992 ,服務端仍是爲8181,一直保持連接沒有斷開。

api說明

屬性

readyState
只讀屬性 readyState 表示鏈接狀態,能夠是如下值:

    • 0 - 表示鏈接還沒有創建。
    • 1 - 表示鏈接已創建,能夠進行通訊。
    • 2 - 表示鏈接正在進行關閉。
    • 3 - 表示鏈接已經關閉或者鏈接不能打開

    bufferedAmount
    只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,可是尚未發出的 UTF-8 文本字節數

    事件
    • open - 鏈接創建時觸發
    • message - 客戶端接收服務端數據時觸發
    • error - 通訊發生錯誤時觸發
    • close - 鏈接關閉時觸發
    方法
    • send - 使用鏈接發送數據
    • close - 關閉鏈接

    瀏覽器支持狀況

    image.png

    小結

    一、websocket 只有一次握手,且是基於http的get 請求實現。二、websocket 客戶端和服務端傳輸消息只能是字符串。三、websocket 爲長鏈接,須要手動斷開,業務上處理聊天時,能夠經過添加心跳來更新有效的鏈接。四、ws 鏈接爲tcp 鏈接,wss 爲tls 鏈接。

    相關文章
    相關標籤/搜索