WebSocket和Socket

Socket

英文socket的意思是插座,網絡中的Socket是一個抽象的接口,能夠理解爲網絡中鏈接的兩端。一般被叫作套接字接口,其意義在對傳輸層進行封裝屏蔽了傳輸層的複雜性。它並非一個協議,是爲了你們更方便的使用傳輸層協議產生的一個抽象層。大部分的主流編程語言都提供socket函數.javascript

WebSocket

說道WebSocket瞭解過一些的人可能會以爲有些高大上的感受,它的誕生還有些故事能夠講,大概是在爲w3c放棄了html後,還有那麼一羣人不服氣(不想放棄),想要繼續推進html發展,同時他們也發展了一些其餘的網絡標準,而且被官方接收。而WebSocket就是其中一種,是爲了建立一種雙向通訊(全雙工)的協議,來做爲HTTP協議的一個替代者,以解決基於http上的長輪詢等技術解決不了(或者解決的不那麼優美)的問題。並且這廝一開始並不叫WebSocket,好像是叫webConnect之類的,最後是一位工程師提議說要麼我們叫WebSocket吧,而後。。。。。,好了故事就這樣,他既然是HTTP的替代者,咱們首先看一下它和HTTP(或者HTTP的長鏈接)的聯繫和區別。html

WebSocket和HTTP 1.1的聯繫

首先二者都是應用層協議,並且 WebSocket 在創建鏈接時,須要借用 http 的 101 switch protocol 來達到協議轉換,爲了創建一個 WebSocket 鏈接,客戶端瀏覽器首先要向服務器發起一個HTTP請求,這個請求和一般的HTTP請求不一樣,包含了一些附加頭信息,其中附加頭信息"Upgrade: WebSocket"和"Connection: Upgrade"代表這是一個申請協議升級的 HTTP 請求,服務器端解析這些附加的頭信息而後產生應答信息返回給客戶端,客戶端和服務器端的 WebSocket 鏈接就創建起來了,在創建鏈接後,就和HTTP沒有關係了,雙方就能夠經過這個鏈接通道自由的傳遞信息。
固然,也有可能服務器不支持WebSocket,那就老老實實的用http吧,目前大部分瀏覽器和服務器都已支持WebSocket。java

貼一段簡單WebSocket客戶端的js代碼nginx

<script type="text/javascript"> //語法 var Socket = new WebSocket(url, [protocol] ); var ws = new WebSocket("ws://localhost:6688/send"); //鏈接創建時觸發 ws.onopen = function(evt) { console.log("Connection open ..."); ws.send("Hello WebSockets!"); }; //接收消息時觸發 ws.onmessage = function(evt) { console.log("Received Message: " + evt.data); ws.close(); }; //關閉鏈接觸發 ws.onclose = function(evt) { console.log("Connection closed."); }; //通訊發生錯誤時觸發 ws.onerror = function(evt) { console.log("Connection Error."); }; //檢查瀏覽器是否支持WebSocket if(typeof WebSocket != 'undefined'){ alert("您的瀏覽器支持 WebSocket!"); }else{ // 瀏覽器不支持 WebSocket alert("您的瀏覽器不支持 WebSocket!"); } </script>

WebSocket和HTTP 1.1區別

咱們來看一下他的格式:web

//一個WebSocket鏈接始於握手(handshake) GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 

對上面狀態進行解釋:
前兩行跟HTTP的Request的起始行同樣,而真正在WS的握手過程當中起到做用的是下面幾個header域。ajax

  • Upgrade:websocket
    upgrade是HTTP1.1中用於定義轉換協議的header域。它表示要升級(轉換)到某個協議(若是服務器支持的話)。sql

  • Connection:Upgrade 表示要進行升級協議編程

  • Sec-WebSocket-Key:用來發送給服務器過濾非預期的請求(好比手動填寫header中的一些信息,但自己不想升級到WebSocket。這時候,因爲Sec-WebSocket-Key和一些相關項被禁止手動設置,因此能夠過濾掉出現非預期的狀況)。瀏覽器

  • Origin:做安全使用,防止跨站攻擊,瀏覽器通常會使用這個來標識原始域。安全

  • Sec-WebSocket-Protocol:客戶端支持的子協議的列表。

  • Sec-WebSocket-Version:客戶端支持的WS協議的版本。

//服務端應答handshake 101表示切換 HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat 

上面是報文區別,還有一些其餘的特性

  • WebSocket的鏈接必須是一個直接鏈接(這個我還須要仔細研究研究,還不是很透徹,若是有懂的朋友能夠幫我理解理解將不勝感謝。回頭我仔細研究一下通訊的方式和數據幀格式)。
  • WebSocket鏈接創建以後,通訊雙方均可以在任什麼時候刻向另外一方發送數據(即全雙工,這是最主要的)。
  • WebSocket鏈接創建以後,數據的傳輸使用幀來傳遞,不一樣於Request。

因爲展開來說的話篇幅太長,你們也能夠自行深刻了解。

WebSocket的全雙工和HTTP

在HTTP中,一個Request對應着一個Response,早期的HTTP1.0每次的HTTP鏈接都須要打開一個TCP鏈接,在一個Request後,服務器產生一個應答Request,此次HTTP鏈接就結束了,同時關閉了TCP鏈接,重複的創建TCP鏈接是一種資源浪費,主動關閉TCP鏈接後還會出現time_wait狀態,繼續佔用資源 一段時間(能夠看上一篇文章TCP鏈接和 time_wait、close_waite
這種狀況在HTTP1.1中進行了必定的改進,使得有一個keep-alive,也就是說,在一個HTTP鏈接中,能夠發送多個Request,接收多個Response,能夠減小創建和拆除TCP鏈接的次數,所以同時減小了time_wait狀態的鏈接,可是,若是設置了keep-alive的超時時間好比nginx中是keepalive_timeout,一段時間沒有通訊超時後服務器主動關閉鏈接也是可能形成服務器出現time_wait狀態的,若是不設置超時時間也會形成必定的資源浪費(佔用鏈接卻不發送數據),因此怎麼設置這個超時時間也很重要。

本質上HTTP1.1中雖然能夠保持持久的鏈接,可是它依然不是全雙工的,由於服務端是不能夠主動給客戶端發送消息的,ajax輪詢的方式雖然能夠達到WebSocket全雙工的相似效果,可是會形成大量的資源浪費。

數據幀格式

0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
  1. 從左到右,單位是比特。好比FIN、RSV一、RSV二、RSV3各佔據1比特,opcode佔據4比特。
  2. 內容包括了標識、操做代碼、掩碼、數據、數據長度等。(下一小節會展開)

各個標誌位

參考上面的數據幀格式來講一下各個字段的含義,重要字段加粗

  • FIN:(finish)1個比特。
    若是是1,表示這是消息的最後一個分片(fragment),若是是0,表示不是最後一個分片。

  • RSV1, RSV2, RSV3:各佔1個比特。
    若不採用WebSocket擴展這裏必須爲0。當客戶端、服務端協商採用WebSocket擴展時,這三個標誌位能夠非0,且值的含義由擴展進行定義。若是出現非零的值,且並無採用WebSocket擴展,鏈接出錯。

  • Opcode: 4個比特。

    操做代碼,定義了對「負載數據」的解釋,Opcode的值決定了應該如何解析後續的數據載荷(data payload)。若是收到一個未知的操做碼,接收端點應該斷開鏈接(fail the connection)。可選的操做代碼以下:

    %x0:表示一個延續幀。當Opcode爲0時,表示本次數據傳輸採用了數據分片,當前收到的數據幀爲其中一個數據分片。
    %x1:表示這是一個文本幀(frame)
    %x2:表示這是一個二進制幀(frame)
    %x3-7:保留的操做代碼,用於後續定義的非控制幀,(通常協議中都會預留出一些碼用於擴展)。
    %x8:表示鏈接斷開/關閉。
    %x9:表示這是一個ping操做。
    %xA:表示這是一個pong操做。
    %xB-F:保留的操做代碼,用於後續定義的控制幀(通常協議中都會預留出一些碼用於擴展)。

  • Mask: 1個比特。
    表示是否要對數據載荷進行掩碼操做。從客戶端向服務端發送數據時,須要對數據進行掩碼操做,Mask須要爲1,masking-key(掩碼鍵)字段存在值;從服務端向客戶端發送數據時,不須要對數據進行掩碼操做,Mask須要爲0。若是服務端接收到的數據沒有進行過掩碼操做,服務端須要斷開鏈接。

  • Payload length:數據載荷的長度,單位是字節。爲7位,或7+16位,或1+64位。

    假設Payload length的值爲x,若是
    x爲0~126:數據的長度爲x字節。
    x爲126:後續2個字節表明一個16位的無符號整數,該無符號整數的值爲數據的長度。
    x爲127:後續8個字節表明一個64位的無符號整數(最高位爲0),該無符號整數的值爲數據的長度。
    上面這種定義負載長度方式是一種網絡協議中經常使用的方法,能夠實現靈活擴展的數據長度定義。

  • Masking-key:0或4字節(32位)
    全部從客戶端傳送到服務端的數據幀,數據載荷都進行了掩碼操做,Mask爲1,且攜帶了4字節的Masking-key。若是Mask爲0,則沒有Masking-key。

  • Payload data:(載荷數據 x+y 字節)
    載荷數據:包括了擴展數據、應用數據。其中,擴展數據x字節,應用數據y字節。載荷數據的長度,不包括mask key的長度。

  • Extension data(擴展數據 x 字節):
    若是沒有協商使用擴展的話,擴展數據數據爲0字節。全部的擴展都必須聲明擴展數據的長度,或者能夠如何計算出擴展數據的長度。此外,擴展如何使用必須在握手階段就協商好。若是擴展數據存在,那麼載荷數據長度必須將擴展數據的長度包含在內。

  • Application data(應用數據 y 字節):
    任意的應用數據在擴展數據以後(若是存在擴展數據),佔據了數據幀剩餘的位置。載荷數據長度 減去 擴展數據長度,就獲得應用數據的長度。

Ping Pang 心跳

對於長時間沒有數據交互的鏈接,會浪費鏈接資源。但不排除有些場景,客戶端、服務端雖然長時間沒有數據往來,但仍須要保持鏈接。這個時候,能夠採用心跳來實現。(不知道心跳爲何是ping、pang)

  1. ping
  • Ping幀操做碼(opcode)0x9。能夠包含「應用數據
  • 當收到一個Ping幀時,接收方必須在響應中發送一個Pong幀,除非它早已接收到一個關閉幀。它應該儘量快地以Pong幀響應。(也用來驗證遠程端點是否可響應)
  1. Pong
    • Pong幀操做碼(opcode)0x9。
    • 一個Pong幀必須攜和被響應的Ping幀中相同的數據
    • 若是再一個ping到達服務端,服務端還沒有響應前,由到達同源的ping幀,則能夠只響應最新的ping幀,
    • 未收到ping也能夠發送一個Pong幀。這個充當單向的心跳(heartbeat),另外一方不須要響應。
相關文章
相關標籤/搜索