Websocket(一)——原理及基本屬性和方法


初次接觸 WebSocket 的人,都會問同樣的問題:我們已經有了 HTTP 協議,為什么還需要另一個協議?它能帶來什么好處?

答案很簡單,因為 HTTP 協議有一個缺陷:通信只能由客戶端發起。

舉例來說,我們想了解今天的天氣,只能是客戶端向服務器發出請求,服務器返回查詢結果。HTTP 協議做不到服務器主動向客戶端推送信息。

 

 

 

WebSocket 協議在2008年誕生,2011年成為國際標准。所有瀏覽器都已經支持了。

它的最大特點就是,服務器可以主動向客戶端推送信息,客戶端也可以主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。

                     

一.傳統的實現即時通信的方式

ajax輪詢

ajax輪詢的原理非常簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。

場景再現:

客戶端:啦啦啦,有沒有新信息(Request)

服務端:沒有(Response)

客戶端:啦啦啦,有沒有新信息(Request)

服務端:沒有。。(Response)

客戶端:啦啦啦,有沒有新信息(Request)

服務端:你好煩啊,沒有啊。。(Response)

客戶端:啦啦啦,有沒有新消息(Request)

服務端:好啦好啦,有啦給你。(Response)

客戶端:啦啦啦,有沒有新消息(Request)

服務端:。。。。。沒。。。。沒。。。沒有(Response) —- loop

long poll

long poll 其實原理跟 ajax輪詢 差不多,都是采用輪詢的方式,不過采取的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起連接后,如果沒消息,就一直不返回Response給客戶端。直到有消息才返回,返回完之后,客戶端再次建立連接,周而復始。

場景再現:

客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request)

服務端:額。。 等待到有消息的時候。。來 給你(Response)

客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request) -loop

從上面可以看出其實這兩種方式,都是在不斷地建立HTTP連接,然后等待服務端處理,可以體現HTTP協議的另外一個特點,被動性。

何為被動性呢,其實就是,服務端不能主動聯系客戶端,只能有客戶端發起。

 

小姐:

ajax輪詢 需要服務器有很快的處理速度和資源。(速度)
long poll 需要有很高的並發,也就是說同時接待客戶的能力。(場地大小)

長連接

在頁面里嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設為對一個長連接的請求或是采用xhr請求,服務器端就能源源不斷地往客戶端輸入數據。
優點:消息即時到達,不發無用請求;管理起來也相對方便。
缺點:服務器維護一個長連接會增加開銷,當客戶端越來越多的時候,server壓力大!
實例:Gmail聊天

 

 (1).基於http協議的長連接

         在HTTP1.0和HTTP1.1協議中都有對長連接的支持。其中HTTP1.0需要在request中增加”Connection: keep-alive“ header才能夠支持,而HTTP1.1默認支持.

      http1.0請求與服務端的交互過程:

      a)客戶端發出帶有包含一個header:”Connection: keep-alive“的請求

      b)服務端接收到這個請求后,根據http1.0和”Connection: keep-alive“判斷出這是一個長連接,就會在response的header中也增加”Connection: keep-alive“,同是不會關閉已建立的tcp連接.

      c)客戶端收到服務端的response后,發現其中包含”Connection: keep-alive“,就認為是一個長連接,不關閉這個連接。並用該連接再發送request.轉到a)

    (2).http1.1請求與服務端的交互過程:

 

    a)客戶端發出http1.1的請求

    b)服務端收到http1.1后就認為這是一個長連接,會在返回的response設置Connection: keep-alive,同是不會關閉已建立的連接.

    c)客戶端收到服務端的response后,發現其中包含”Connection: keep-alive“,就認為是一個長連接,不關閉這個連接。並用該連接再發送request.轉到a)

     基於http協議的長連接減少了請求,減少了建立連接的時間,但是每次交互都是由客戶端發起的,客戶端發送消息,服務端才能返回客戶端消息.因為客戶端也不知道服務端什么時候會把結果准備好,所以客戶端的很多請求是多余的,僅是維持一個心跳,浪費了帶寬.

 

Flash Socket

在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript通過調用此Flash程序提供的Socket接口與服務器端的Socket接口進行通信,JavaScript在收到服務器端傳送的信息后控制頁面的顯示。
優點:實現真正的即時通信,而不是偽即時。
缺點:客戶端必須安裝Flash插件,移動端支持不好,IOS系統中沒有flash的存在;非HTTP協議,無法自動穿越防火牆。
實例:網絡互動游戲。

 

二.websocket的方式實現服務端消息推送

 

1.什么是socket?什么是websocket?兩者有什么區別?websocket是僅僅將socket的概念移植到瀏覽器中的實現嗎?

 

  我們知道,在網絡中的兩個應用程序(進程)需要全雙工相互通信(全雙工即雙方可同時向對方發送消息),需要用到的就是socket,它能夠提供端對端通信,對於程序員來講,他只需要在某個應用程序的一端(暫且稱之為客戶端)創建一個socket實例並且提供它所要連接一端(暫且稱之為服務端)的IP地址和端口,而另外一端(服務端)創建另一個socket並綁定本地端口進行監聽,然后客戶端進行連接服務端,服務端接受連接之后雙方建立了一個端對端的TCP連接,在該連接上就可以雙向通訊了,而且一旦建立這個連接之后,通信雙方就沒有客戶端服務端之分了,提供的就是端對端通信了。我們可以采取這種方式構建一個桌面版的im程序,讓不同主機上的用戶發送消息。從本質上來說,socket並不是一個新的協議,它只是為了便於程序員進行網絡編程而對tcp/ip協議族通信機制的一種封裝。

 

socket傳送門:http://blog.csdn.net/luokehua789789/article/details/54378264

websocket是html5規范中的一個部分,它借鑒了socket這種思想,為web應用程序客戶端和服務端之間(注意是客戶端服務端)提供了一種全雙工通信機制。同時,它又是一種新的應用層協議,websocket協議是為了提供web應用程序和服務端全雙工通信而專門制定的一種應用層協議,通常它表示為:ws://echo.websocket.org/?encoding=text HTTP/1.1,可以看到除了前面的協議名和http不同之外,它的表示地址就是傳統的url地址。

Websocket其實是一個新協議,跟HTTP協議基本沒有關系,只是為了兼容現有瀏覽器的握手規范而已,也就是說它是HTTP協議上的一種補充可以通過這樣一張圖理解

 

websocket具有以下幾個方面的優勢:

(1)建立在 TCP 協議之上,服務器端的實現比較容易。

(2)與 HTTP 協議有着良好的兼容性。默認端口也是80和443,並且握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器。

(3)數據格式比較輕量,性能開銷小,通信高效。

(4)可以發送文本,也可以發送二進制數據。

(5)沒有同源限制,客戶端可以與任意服務器通信。

(6)協議標識符是ws(如果加密,則為wss),服務器網址就是 URL。

 

 

2.websocket的通信原理和機制

既然是基於瀏覽器端的web技術,那么它的通信肯定少不了http,websocket本身雖然也是一種新的應用層協議,但是它也不能夠脫離http而單獨存在。具體來講,我們在客戶端構建一個websocket實例,並且為它綁定一個需要連接到的服務器地址,當客戶端連接服務端的時候,會向服務端發送一個類似下面的http報文

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
 
        
Upgrade: websocket
Connection: Upgrade

 

 

這個就是Websocket的核心了,告訴Apache、Nginx等服務器:發起的是websocket協議。

 

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

 

 

 

首先,Sec-WebSocket-Key  是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:泥煤,不要忽悠窩,我要驗證尼是不是真的是Websocket助理。
然后,Sec_WebSocket-Protocol  是一個用戶定義的字符串,用來區分同URL下,不同的服務所需要的協議。簡單理解:今晚我要服務A,別搞錯啦~
最后,Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各種奇奇怪怪的協議都有,而且還有很多期奇奇怪怪不同的東西,什么Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多可是一個大難題。。不過現在還好,已經定下來啦~大家都使用的一個東西~ 脫水:服務員,我要的是13歲的噢→_→

然后服務器會返回下列東西,表示已經接受到請求, 成功建立Websocket啦!

 

 

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

 

 

 

 

依然是固定的,告訴客戶端即將升級的是Websocket協議,而不是mozillasocket,lurnarsocket或者shitsocket。
然后,Sec-WebSocket-Accept 這個則是經過服務器確認,並且加密過后的 Sec-WebSocket-Key。服務器:好啦好啦,知道啦,給你看我的ID CARD來證明行了吧。。
后面的,Sec-WebSocket-Protocol 則是表示最終使用的協議。

返回的狀態碼為101,表示同意客戶端協議轉換請求,並將它轉換為websocket協議。以上過程都是利用http通信完成的,稱之為websocket協議握手(websocket Protocol handshake),進過這握手之后,客戶端和服務端就建立了websocket連接,以后的通信走的都是websocket協議了。所以總結為websocket握手需要借助於http協議,建立連接后通信過程使用websocket協議。同時需要了解的是,該websocket連接還是基於我們剛才發起http連接的那個TCP連接。一旦建立連接之后,我們就可以進行數據傳輸了,websocket提供兩種數據傳輸:文本數據和二進制數據。

 

至此,HTTP已經完成它所有工作了,接下來就是完全按照Websocket協議進行了。

基於以上分析,我們可以看到,websocket能夠提供低延遲,高性能的客戶端與服務端的雙向數據通信。它顛覆了之前web開發的請求處理響應模式,並且提供了一種真正意義上的客戶端請求,服務器推送數據的模式,特別適合實時數據交互應用開發。

對比前面的http的客戶端服務器的交互圖可以發現WebSocket方式減少了很多TCP打開和關閉連接的操作,WebSocket的資源利用率高。

 

3.websocket的創建和常用的屬性方法

以下 API 用於創建 WebSocket 對象。

var Socket = new WebSocket(url, [protocol] );

以上代碼中的第一個參數 url, 指定連接的 URL。第二個參數 protocol 是可選的,指定了可接受的子協議。

 

WebSocket 屬性

以下是 WebSocket 對象的屬性。假定我們使用了以上代碼創建了 Socket 對象:

屬性 描述
Socket.readyState

只讀屬性 readyState 表示連接狀態,可以是以下值:

  • 0 - 表示連接尚未建立。

  • 1 - 表示連接已建立,可以進行通信。

  • 2 - 表示連接正在進行關閉。

  • 3 - 表示連接已經關閉或者連接不能打開。

Socket.bufferedAmount

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

 

 

 

 

 

 

 

 

 

CONNECTING:值為0,表示正在連接。

OPEN:值為1,表示連接成功,可以通信了。

CLOSING:值為2,表示連接正在關閉。

CLOSED:值為3,表示連接已經關閉,或者打開連接失敗。

 var webSocket = new WebSocket(url);
  if(webSocket.readyState == webSocket.CONNECTING){
    console.log('連接正在打開');
  }

  webSocket.onopen = function () {
    webSocket.send(consumerId);
    //可以看到 "連接正在打開"並沒有被打印,說明open對應的就是OPEN狀態;
    if(webSocket.readyState == webSocket.CONNECTING){
      console.log('連接正在打開1');
    }
    if(webSocket.readyState == webSocket.OPEN){
      console.log('連接已打開');
    }
    sendMsg();
    window.weui.alert('已經建立連接');
  };


//連接關閉時觸發
  webSocket.onclose = function () {
    if(webSocket.readyState == webSocket.CLOSED){
      console.log('連接已關閉')
    }
      window.weui.alert('連接已斷開');
  };

  //連接
  webSocket.onerror = function () {
    window.weui.alert('連接錯誤,請稍后再試');
  };

 

 

 

 

可以看到,當onopen觸發時,對應的額就是readyState的OPEN狀態,不包含OPENING;onclose觸發時,對應的就是CLOSED狀態,不包含CLOSING狀態。

WebSocket 事件

以下是 WebSocket 對象的相關事件。假定我們使用了以上代碼創建了 Socket 對象:

事件 事件處理程序 描述
open Socket.onopen 連接建立時觸發
message Socket.onmessage 客戶端接收服務端數據時觸發
error Socket.onerror 通信發生錯誤時觸發
close Socket.onclose 連接關閉時觸發

 

 

 

 

WebSocket 方法

以下是 WebSocket 對象的相關方法。假定我們使用了以上代碼創建了 Socket 對象:

方法 描述
Socket.send()

使用連接發送數據

Socket.close()

關閉連接

 

 

 

 

 

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>菜鳥教程(runoob.com)</title>
    
      <script type="text/javascript">
         function WebSocketTest()
         {
            if ("WebSocket" in window)
            {
               alert("您的瀏覽器支持 WebSocket!");
               
               // 打開一個 web socket
               var ws = new WebSocket("ws://localhost:9998/echo");
                
               ws.onopen = function()
               {
                  // Web Socket 已連接上,使用 send() 方法發送數據
                  ws.send("發送數據");
                  alert("數據發送中...");
               };
                
               ws.onmessage = function (evt) 
               { 
                  var received_msg = evt.data;
                  alert("數據已接收...");
               };
                
               ws.onclose = function()
               { 
                  // 關閉 websocket
                  alert("連接已關閉..."); 
               };
            }
            
            else
            {
               // 瀏覽器不支持 WebSocket
               alert("您的瀏覽器不支持 WebSocket!");
            }
         }
      </script>
        
   </head>
   <body>
   
      <div id="sse">
         <a href="javascript:WebSocketTest()">運行 WebSocket</a>
      </div>
      
   </body>
</html>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

用websocket發送接受二進制數據

WebSocket可以通過ArrayBuffer,發送或接收二進制數據。

var socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';

// Wait until socket is open
socket.addEventListener('open', function (event) {
  // Send binary data
  var typedArray = new Uint8Array(4);
  socket.send(typedArray.buffer);
});

// Receive binary data
socket.addEventListener('message', function (event) {
  var arrayBuffer = event.data;
  // ···
});

 

 

 

 

 

 

 

目前websocket的缺點是不兼容低版本瀏覽器。

WebSocket 有沒有可能取代 AJAX ?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM