WebSocket和Socket


Socket

英文socket的意思是插座,網絡中的Socket是一個抽象的接口,可以理解為網絡中連接的兩端。通常被叫做套接字接口,其意義在對傳輸層進行封裝屏蔽了傳輸層的復雜性。它並不是一個協議,是為了大家更方便的使用傳輸層協議產生的一個抽象層。大部分的主流編程語言都提供socket函數.

WebSocket

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

WebSocket和HTTP 1.1的聯系

首先兩者都是應用層協議,而且 WebSocket 在建立連接時,需要借用 http 的 101 switch protocol 來達到協議轉換,為了建立一個 WebSocket 連接,客戶端瀏覽器首先要向服務器發起一個HTTP請求,這個請求和通常的HTTP請求不同,包含了一些附加頭信息,其中附加頭信息"Upgrade: WebSocket"和"Connection: Upgrade"表明這是一個申請協議升級的 HTTP 請求,服務器端解析這些附加的頭信息然后產生應答信息返回給客戶端,客戶端和服務器端的 WebSocket 連接就建立起來了,在建立連接后,就和HTTP沒有關系了,雙方就可以通過這個連接通道自由的傳遞信息。
當然,也有可能服務器不支持WebSocket,那就老老實實的用http吧,目前大部分瀏覽器和服務器都已支持WebSocket。

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

<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區別

我們來看一下他的格式:

//一個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域。

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

  • 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、RSV1、RSV2、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),另一方不需要響應。


免責聲明!

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



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