websocket之一:websocket簡介


Websocket

websocket為一次HTTP握手后,后續通訊為tcp協議的通訊方式。

WebSocket 使用一種被稱作 “Upgrade handshake(升級握手)”的機制將標准的 HTTP 或HTTPS 協議轉為 WebSocket。因此,使用 WebSocket 的應用程序將始終以 HTTP/S 開始,然后進行升級。這種升級發生在什么時候取決於具體的應用;可以在應用啟動的時候,或者當一個特定的 URL 被請求的時候。
在我們的應用中,僅當 URL 請求以“/ws”結束時,我們才升級協議為WebSocket。否則,服務器將使用基本的 HTTP/S。一旦連接升級,之后的數據傳輸都將使用 WebSocket 。

當然,和HTTP一樣,websocket也有一些約定的通訊方式,http通訊方式為http開頭的方式,e.g. http://xxx.com/path ,websocket通訊方式則為ws開頭的方式,e.g. ws://xxx.com/path

SSL:

  1. HTTP: https://xxx.com/path
  2. WEBSOCKET: wss://xxx.com/path

 

下面的是Netty中Websocket的邏輯圖:

 

 

一、websocket的特點

1.2、websocket與http

WebSocket是HTML5出的東西(協議),也就是說HTTP協議沒有變化,或者說沒關系,但HTTP是不支持持久連接的(長連接,循環連接的不算)

首先HTTP有 1.1 和 1.0 之說,也就是所謂的 keep-alive ,把多個HTTP請求合並為一個,但是 Websocket 其實是一個新協議,跟HTTP協議基本沒有關系,只是為了兼容現有瀏覽器的握手規范而已,也就是說它是HTTP協議上的一種補充可以通過這樣一張圖理解

有交集,但是並不是全部。

共同點是:都是基於TCP協議進行client-server的鏈接,websocket是HTML5提出的一套補缺HTTP鏈接中不能持久鏈接的特點(除長連接,長輪詢)。

回顧下下面幾個概念:

  • 輪詢(AJAX):指的是瀏覽器端定時發送請求到服務器端,服務器接收到請求后作出響應並關閉連接該次鏈接。(缺點是:在時間定時上需要考慮更多,多久進行一次輪詢等)
  • 長輪詢:跟輪詢的原理相似,但是有個重要的特點是,瀏覽器發送請求后,服務器此時是保持該鏈接(主要方法是死循環執行,定時sleep超時后斷開連接),在定時斷開鏈接之前,獲取到最新數據並返回瀏覽器后,在由瀏覽器重新發起一個新的請求。    (缺點是:會並發性的發起很多的http請求。)

    輪詢與長輪詢共同的缺點是:需要浪費很多的http資源,請求的數量較大。

  • 長連接:類似前端中跨域請求中,利用iframe的src的跨域特點,在頁面中隱藏一個iframe進行定時獲取后台數據,反饋到iframe中,實現后台數據的獲取。( 長鏈接的缺點也是:必須要定時發起請求服務器更新數據資源。)

 

websocket解決的問題:

實質的推送方式是服務器主動推送,只要有數據就推送到請求方。(變被動為主動)

websocket采用異步回調的方式接受消息,當建立通信連接,可以做到持久性的連接,並進行通信。而不像上面的幾種方式一樣需要定時進行發起請求到服務器獲取最新更新信息,顯得相當的被動)

websocket通過自己的 WS 協議(此處與HTTP協議有所區別)創建一個基於HTTP request請求並創建TCP鏈接之后,之后的數據交換都不需要再次去創建鏈接,實現真正的長連接。

websocket協議本質上是一個基於TCP的協議。建立連接需要握手,客戶端(瀏覽器)首先向服務器(web server)發起一條特殊的http請求,web server解析后生成應答到瀏覽器,這樣子一個websocket連接就建立了,直到某一方關閉連接。

當然基於Node.js編寫的一個Socket.IO 是一個開源實現WebSocket的庫,它通過node.js實現服務端的同時,也提供了客戶端js庫,socket.io 支持事件觸發為基礎進行的雙向數據通信。

與HTTP比較

同樣作為應用層的協議,WebSocket在現代的軟件開發中被越來越多的實踐,和HTTP有很多相似的地方,這里將它們簡單的做一個純個人、非權威的比較:

相同點

  1. 都是基於TCP的應用層協議。
  2. 都使用Request/Response模型進行連接的建立。
  3. 在連接的建立過程中對錯誤的處理方式相同,在這個階段WS可能返回和HTTP相同的返回碼。
  4. 都可以在網絡中傳輸數據。

不同點

  1. WS使用HTTP來建立連接,但是定義了一系列新的header域,這些域在HTTP中並不會使用。
  2. WS的連接不能通過中間人來轉發,它必須是一個直接連接。
  3. WS連接建立之后,通信雙方都可以在任何時刻向另一方發送數據。
  4. WS連接建立之后,數據的傳輸使用幀來傳遞,不再需要Request消息。
  5. WS的數據幀有序。

 

HTTP

HTTP1.1的連接默認使用持續連接(persistent connection),持續連接指的是,有時是客戶端會需要在短時間內向服務端請求大量的相關的資源,如果不是持續連接,那么每個資源都要建立一個新的連接,HTTP底層使用的是TCP,那么每次都要使用三次握手建立TCP連接,將造成極大的資源浪費。

持續連接可以帶來很多的好處:

  1. 使用更少的TCP連接,對通信各方的壓力更小。
  2. 可以使用管道(pipeline)來傳輸信息,這樣請求方不需要等待結果就可以發送下一條信息,對於單個的TCP的使用更充分。
  3. 流量更小
  4. 順序請求的延時更小。
  5. 不需要重新建立TCP連接就可以傳送error,關閉連接等信息。

HTTP1.1的服務器使用TCP的流量控制來控制HTTP的流量,HTTP1.1的客戶端在收到服務器連接中發過來的error信息,就要馬上關閉此鏈接。關於HTTP連接還有很多細節,之后再詳述。

 

1.2、websocket與socket

短答案

就像Java和JavaScript,並沒有什么太大的關系,但又不能說完全沒關系。可以這么說:

  • 命名方面,Socket是一個深入人心的概念,WebSocket借用了這一概念;
  • 使用方面,完全兩個東西。

長答案

當我們探討兩件事物的區別和聯系時,我們想探討些什么?

對於我來說,大多數情況是想知道兩件事物本身,而並不是想只想了解「區別」本身。那么對這個問題最直接的解決方法應該是去了解Socket和WebSocket的來源和用法,那么它們的區別和聯系就不言自明了。

Socket

Socket又稱"套接字",應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。Socket的英文原義是“孔”或“插座”,作為UNIX的進程通信機制。Socket可以實現應用程序間網絡通信。

Socket可以使用TCP/IP協議或UDP協議。

TCP/IP協議

TCP/IP協議是目前應用最為廣泛的協議,是構成Internet國際互聯網協議的最為基礎的協議,由TCP和IP協議組成:
TCP協議:面向連接的、可靠的、基於字節流的傳輸層通信協議,負責數據的可靠性傳輸的問題。

IP協議:用於報文交換網絡的一種面向數據的協議,主要負責給每台網絡設備一個網絡地址,保證數據傳輸到正確的目的地。

UDP協議

UDP特點:無連接、不可靠、基於報文的傳輸層協議,優點是發送后不用管,速度比TCP快。

 

WebSocket

上邊簡單敘述了Socket的意義,由於年代久遠,很多事情也搞不了那么清楚。但WebSocket是一個很晚近的東西,可以讓我們看到它是如何成為現在我們看到的這個樣子的。

WHATWG(Web Hypertext Application Technology Working Group)

關於HTML5的故事很多人都是知道的,w3c放棄了HTML,然后有一群人(也有說是這些人供職的公司,不過官方的文檔上是說的個人)創立了WHATWG組織來推動HTML語言的繼續發展,同時,他們還發展了很多關於Web的技術標准,這些標准不斷地被官方所接受。WebSocket就屬於WHATWG發布的Web Application的一部分(即HTML5)的產物。

為什么會有WebSocket

大約在08年的時候,WG的工程師在討論網絡環境中需要一種全雙工的連接形式,剛開始一直叫做「TCPConnection」,並討論了這種協議需要支持的功能,大致已經和我們今天看到的WebSocket差不多了。他們認為基於現有的HTTP之上的一些技術(如長輪詢、Comet)並滿足不了這種需求,有必要定義一個全新的協議。

名稱的由來

在很多的關於HTML5或者WebSocket的文檔中,都能看到一個名字,Hixie(Ian Hickson),他是WHATWG組織的發言人,曾供職於Netscape、Opera、Google,看工作的公司就知道這個人的背景了。

hixie

08年6月18日,一群WHATWG的工程師在討論一些技術問題,一個工程師提到說「我們之前討論的那個東西,不要叫TCPConnection 了,還是起個別的名字吧 」,接着幾個名字被提及,DuplexConnection,TCPSocket,SocketConnection ,一個叫mcarter(Michael Carter )的工程師說他馬上要寫一篇關於Comet的文章,如果可以確定這個名稱,想在文章中引用這個名字。

Socket一直以來都被人用來表示網絡中一個連接的兩端,考慮到怎么讓工程師更容易接受,后來Hixie說了一句「我看WebSocket這個名字就很適合嘛(Hixie briefly pops back online to record that "WebSocket" would probably be a good new name for the TCPConnection object)」,大家都沒有異議,緊接着mcarter在Comet Daily中發表了文章Independence Day: HTML5 WebSocket Liberates Comet From Hacks,后來隨着各大瀏覽器對WebSocket的支持,它變成了實際的標准,IETF也沿用了這個名字。

下邊是在WHATWG文檔中對WebSocket接口的定義

enum BinaryType { "blob", "arraybuffer" };
[Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []), Exposed=(Window,Worker)]
interface WebSocket : EventTarget {
  readonly attribute USVString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long long bufferedAmount;

  // networking
  attribute EventHandler onopen;
  attribute EventHandler onerror;
  attribute EventHandler onclose;
  readonly attribute DOMString extensions;
  readonly attribute DOMString protocol;
  void close([Clamp] optional unsigned short code, optional USVString reason);

  // messaging
  attribute EventHandler onmessage;
  attribute BinaryType binaryType;
  void send(USVString data);
  void send(Blob data);
  void send(ArrayBuffer data);
  void send(ArrayBufferView data);
};

接口的內容可以分為三類:狀態變量、網絡功能和消息處理等

  1. 構造函數WebSocket(url, protocols):構造WebSocket對象,以及建立和服務器連接; protocols可選字段,代表選擇的子協議
  2. 狀態變量readyState: 代表當前連接的狀態,短整型數據,取值為CONNECTING(值為0), OPEN(值為1), CLOSING(值為2), CLOSED(值為3)
  3. 方法變量close(code, reason): 關閉此WebSocket連接。
  4. 狀態變量bufferedAmount: send函數調用后,被緩存並且未發送到網絡上的數據長度
  5. 方法變量send(data): 將數據data通過此WebSocket發送到對端
  6. 回調函數onopen/onmessage/onerror/onclose: 當相應的事件發生時會觸發此回調函數

內容的確定

大多數新技術的出現都是建立在已有技術的鋪墊之上的,WebSocket內容的確定也是如此,其中就有Comet看不到的貢獻,Comet是一個很有趣的技術,有興趣可以看看這里

結論

可以把WebSocket想象成HTTP,HTTP和Socket什么關系,WebSocket和Socket就是什么關系。

1.3、WebSocket

只從RFC發布的時間看來,WebSocket要晚近很多,HTTP 1.1是1999年,WebSocket則是12年之后了。WebSocket協議的開篇就說,本協議的目的是為了解決基於瀏覽器的程序需要拉取資源時必須發起多個HTTP請求和長時間的輪訓的問題而創建的。

WebSocket協議還很年輕,RFC文檔相比HTTP的發布時間也很短,它的誕生是為了創建一種「雙向通信」的協議,來作為HTTP協議的一個替代者。那么首先看一下它和HTTP(或者HTTP的長連接)的區別。

1.4、為什么要用WebSocket來替代HTTP

上一篇中提到WebSocket的目的就是解決網絡傳輸中的雙向通信的問題,HTTP1.1默認使用持久連接(persistent connection),在一個TCP連接上也可以傳輸多個Request/Response消息對,但是HTTP的基本模型還是一個Request對應一個Response。這在雙向通信(客戶端要向服務器傳送數據,同時服務器也需要實時的向客戶端傳送信息,一個聊天系統就是典型的雙向通信)時一般會使用這樣幾種解決方案:

  1. 輪詢(polling),輪詢就會造成對網絡和通信雙方的資源的浪費,且非實時。
  2. 長輪詢,客戶端發送一個超時時間很長的Request,服務器hold住這個連接,在有新數據到達時返回Response,相比#1,占用的網絡帶寬少了,其他類似。
  3. 長連接,其實有些人對長連接的概念是模糊不清的,我這里講的其實是HTTP的長連接(1)。如果你使用Socket來建立TCP的長連接(2),那么,這個長連接(2)跟我們這里要討論的WebSocket是一樣的,實際上TCP長連接就是WebSocket的基礎,但是如果是HTTP的長連接,本質上還是Request/Response消息對,仍然會造成資源的浪費、實時性不強等問題。

HTTP的長連接模型

websocket缺點:

少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別。

 

二、協議基礎

WebSocket的目的是取代HTTP在雙向通信場景下的使用,而且它的實現方式有些也是基於HTTP的(WS的默認端口是80443)。現有的網絡環境(客戶端、服務器、網絡中間人、代理等)對HTTP都有很好的支持,所以這樣做可以充分利用現有的HTTP的基礎設施,有點向下兼容的意味。

簡單來講,WS協議有兩部分組成:握手和數據傳輸

2.1、握手(handshake)

出於兼容性的考慮,WS的握手使用HTTP來實現(此文檔中提到未來有可能會使用專用的端口和方法來實現握手),客戶端的握手消息就是一個「普通的,帶有Upgrade頭的,HTTP Request消息」。所以這一個小節到內容大部分都來自於RFC2616,這里只是它的一種應用形式,下面是RFC6455文檔中給出的一個客戶端握手消息示例:

    GET /chat HTTP/1.1                //1
    Host: server.example.com          //2
    Upgrade: websocket                //3
    Connection: Upgrade               //4
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==            //5
    Origin: http://example.com                             //6
    Sec-WebSocket-Protocol: chat, superchat                //7
    Sec-WebSocket-Version: 13                              //8

可以看到,前兩行跟HTTP的Request的起始行一模一樣,而真正在WS的握手過程中起到作用的是下面幾個header域。

  1. Upgrade:upgrade是HTTP1.1中用於定義轉換協議的header域。它表示,如果服務器支持的話,客戶端希望使用現有的「網絡層」已經建立好的這個「連接(此處是TCP連接)」,切換到另外一個「應用層」(此處是WebSocket)協議。

  2. Connection:HTTP1.1中規定Upgrade只能應用在「直接連接」中,所以帶有Upgrade頭的HTTP1.1消息必須含有Connection頭,因為Connection頭的意義就是,任何接收到此消息的人(往往是代理服務器)都要在轉發此消息之前處理掉Connection中指定的域(不轉發Upgrade域)。
    如果客戶端和服務器之間是通過代理連接的,那么在發送這個握手消息之前首先要發送CONNECT消息來建立直接連接。

  3. 首先, Sec-WebSocket-Key 是一個 Base64 encode 的值,這個是瀏覽器隨機生成的,發送給服務器使用(服務器會使用此字段組裝成另一個key值放在握手返回信息里發送客戶端)。用於驗證服務端是不是真的是Websocket代理。

    然后, Sec_WebSocket-Protocol 是一個用戶定義的字符串,標識了客戶端支持的子協議的列表。用來區分同URL下,不同的服務所需要的協議。

    最后, Sec-WebSocket-Version 是告訴服務器所使用的 Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各種奇奇怪怪的協議都有,而且還有很多期奇奇怪怪不同的東西,什么Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多可是一個大難題。

  4. Origin:作安全使用,防止跨站攻擊,瀏覽器一般會使用這個來標識原始域。

如果服務器接受了這個請求,可能會發送如下這樣的返回信息,這是一個標准的HTTP的Response消息。101表示服務器收到了客戶端切換協議的請求,並且同意切換到此協議。RFC2616規定只有切換到的協議「比HTTP1.1更好」的時候才能同意切換。

    HTTP/1.1 101 Switching Protocols //1
    Upgrade: websocket.              //2
    Connection: Upgrade.             //3
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=  //4
    Sec-WebSocket-Protocol: chat.    //5

1.第1行,101表示服務器收到了客戶端切換協議的請求,並且同意切換到此協議。

2.第2,3行,依然是固定的,告訴客戶端即將升級的是 Websocket 協議。

3.第4行,這個則是經過服務器確認,並且加密過后的 Sec-WebSocket-Key。

4.第5行,則是表示最終使用的協議。

WebSocket協議Uri

ws協議默認使用80端口,wss協議默認使用443端口。

      ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
      wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

      host = <host, defined in [RFC3986], Section 3.2.2>
      port = <port, defined in [RFC3986], Section 3.2.3>
      path = <path-abempty, defined in [RFC3986], Section 3.3>
      query = <query, defined in [RFC3986], Section 3.4>
在客戶端發送握手之前要做的一些小事

在握手之前,客戶端首先要先建立連接,一個客戶端對於一個相同的目標地址(通常是域名或者IP地址,不是資源地址)同一時刻只能有一個處於CONNECTING狀態(就是正在建立連接)的連接。從建立連接到發送握手消息這個過程大致是這樣的:

  1. 客戶端檢查輸入的Uri是否合法。
  2. 客戶端判斷,如果當前已有指向此目標地址(IP地址)的連接(A)仍處於CONNECTING狀態,需要等待這個連接(A)建立成功,或者建立失敗之后才能繼續建立新的連接。
    PS:如果當前連接是處於代理的網絡環境中,無法判斷IP地址是否相同,則認為每一個Host地址為一個單獨的目標地址,同時客戶端應當限制同時處於CONNECTING狀態的連接數。
    PPS:這樣可以防止一部分的DDOS攻擊。
    PPPS:客戶端並不限制同時處於「已成功」狀態的連接數,但是如果一個客戶端「持有大量已成功狀態的連接的」,服務器或許會拒絕此客戶端請求的新連接。
  3. 如果客戶端處於一個代理環境中,它首先要請求它的代理來建立一個到達目標地址的TCP連接。
    例如,如果客戶端處於代理環境中,它想要連接某目標地址的80端口,它可能要收現發送以下消息:

    CONNECT example.com:80 HTTP/1.1
    Host: example.com

    如果客戶端沒有處於代理環境中,它就要首先建立一個到達目標地址的直接的TCP連接。

  4. 如果上一步中的TCP連接建立失敗,則此WebSocket連接失敗。
  5. 如果協議是wss,則在上一步建立的TCP連接之上,使用TSL發送握手信息。如果失敗,則此WebSocket連接失敗;如果成功,則以后的所有數據都要通過此TSL通道進行發送。
對於客戶端握手信息的一些小要求
  1. 握手必須是RFC2616中定義的Request消息
  2. 此Request消息的方法必須是GET,HTTP版本必須大於1.1 。
    以下是某WS的Uri對應的Request消息:
     ws://example.com/chat
     GET /chat HTTP/1.1
  3. 此Request消息中Request-URI部分(RFC2616中的概念)所定義的資型必須和WS協議的Uri中定義的資源相同。
  4. 此Request消息中必須含有Host頭域,其內容必須和WS的Uri中定義的相同。
  5. 此Request消息必須包含Upgrade頭域,其內容必須包含websocket關鍵字。
  6. 此Request消息必須包含Connection頭域,其內容必須包含Upgrade指令。
  7. 此Request消息必須包含Sec-WebSocket-Key頭域,其內容是一個Base64編碼的16位隨機字符。
  8. 如果客戶端是瀏覽器,此Request消息必須包含Origin頭域,其內容是參考RFC6454
  9. 此Request消息必須包含Sec-WebSocket-Version頭域,在此協議中定義的版本號是13。
  10. 此Request消息可能包含Sec-WebSocket-Protocol頭域,其意義如上文中所述。
  11. 此Request消息可能包含Sec-WebSocket-Extensions頭域,客戶端和服務器可以使用此header來進行一些功能的擴展。
  12. 此Request消息可能包含任何合法的頭域。如RFC2616中定義的那些。
在客戶端接收到Response握手消息之后要做的一些事情
  1. 如果返回的返回碼不是101,則按照RFC2616進行處理。如果是101,進行下一步,開始解析header域,所有header域的值不區分大小寫。
  2. 判斷是否含有Upgrade頭,且內容包含websocket。
  3. 判斷是否含有Connection頭,且內容包含Upgrade
  4. 判斷是否含有Sec-WebSocket-Accept頭,其內容在下面介紹。
  5. 如果含有Sec-WebSocket-Extensions頭,要判斷是否之前的Request握手帶有此內容,如果沒有,則連接失敗。
  6. 如果含有Sec-WebSocket-Protocol頭,要判斷是否之前的Request握手帶有此協議,如果沒有,則連接失敗。
服務端的概念

服務端指的是所有參與處理WebSocket消息的基礎設施,比如如果某服務器使用Nginx(A)來處理WebSocket,然后把處理后的消息傳給響應的服務器(B),那么A和B都是這里要討論的服務端的范疇。

接受了客戶端的連接請求,服務端要做的一些事情

如果請求是HTTPS,則首先要使用TLS進行握手,如果失敗,則關閉連接,如果成功,則之后的數據都通過此通道進行發送。

之后服務端可以進行一些客戶端驗證步驟(包括對客戶端header域的驗證),如果需要,則按照RFC2616來進行錯誤碼的返回。

如果一切都成功,則返回成功的Response握手消息。

服務端發送的成功的Response握手

此握手消息是一個標准的HTTP Response消息,同時它包含了以下幾個部分:

  1. 狀態行(如上一篇RFC2616中所述)
  2. Upgrade頭域,內容為websocket
  3. Connection頭域,內容為Upgrade
  4. Sec-WebSocket-Accept頭域,其內容的生成步驟:
    1. 首先將Sec-WebSocket-Key的內容加上字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11(一個UUID)。
    2. 將#1中生成的字符串進行SHA1編碼。
    3. 將#2中生成的字符串進行Base64編碼。
  5. Sec-WebSocket-Protocol頭域(可選)
  6. Sec-WebSocket-Extensions頭域(可選)

一旦這個握手發出去,服務端就認為此WebSocket連接已經建立成功,處於OPEN狀態。它就可以開始發送數據了。

WebSocket的一些擴展

Sec-WebSocket-Version可以被通信雙方用來支持更多的協議的擴展,RFC6455中定義的值為13,WebSocket的客戶端和服務端可能回自定義更多的版本號來支持更多的功能。其使用方法如上文所述。

2.2、發送數據

WebSocket中所有發送的數據使用幀的形式發送。客戶端發送的數據幀都要經過掩碼處理,服務端發送的所有數據幀都不能經過掩碼處理。否則對方需要發送關閉幀。

一個幀包含一個幀類型的標識碼,一個負載長度,和負載。負載包括擴展內容和應用內容。

幀類型

幀類型是由一個4位長的叫Opcode的值表示,任何WebSocket的通信方收到一個位置的幀類型,都要以連接失敗的方式斷開此連接。
RFC6455中定義的幀類型如下所示:

  1. Opcode == 0 繼續

    表示此幀是一個繼續幀,需要拼接在上一個收到的幀之后,來組成一個完整的消息。由於這種解析特性,非控制幀的發送和接收必須是相同的順序。

  2. Opcode == 1 文本幀
  3. Opcode == 2 二進制幀
  4. Opcode == 3 - 7 未來使用(非控制幀)
  5. Opcode == 8 關閉連接(控制幀)
    此幀可能會包含內容,以表示關閉連接的原因。
    通信的某一方發送此幀來關閉WebSocket連接,收到此幀的一方如果之前沒有發送此幀,則需要發送一個同樣的關閉幀以確認關閉。如果雙方同時發送此幀,則雙方都需要發送回應的關閉幀。
    理想情況服務端在確認WebSocket連接關閉后,關閉相應的TCP連接,而客戶端需要等待服務端關閉此TCP連接,但客戶端在某些情況下也可以關閉TCP連接。
  6. Opcode == 9 Ping
    類似於心跳,一方收到Ping,應當立即發送Pong作為響應。
  7. Opcode == 10 Pong
    如果通信一方並沒有發送Ping,但是收到了Pong,並不要求它返回任何信息。Pong幀的內容應當和收到的Ping相同。可能會出現一方收到很多的Ping,但是只需要響應最近的那一次就可以了。
  8. Opcode == 11 - 15 未來使用(控制幀)

目前有6種幀是可用的。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 ...                |
 +---------------------------------------------------------------+

其中最重要的字段為opcode(4bit)和MASK(1bit):

  • MASK值,從客戶端進行發送的幀必須置此位為1,從服務器發送的幀必須置為0。如果任何一方收到的幀不符合此要求,則發送關閉幀(Close frame)關閉連接。
  • opcode的值: 0x1代表此幀為文本數據幀, 0x2代表此幀為二進制數據幀, 0x8為控制幀中的連接關閉幀(close frame), 0x9為控制幀中的Ping幀, 0xA(十進制的10)為控制幀中的Pong幀。
  • Ping/Pong幀: Ping幀和Pong幀用於連接的保活(keepalive)或者診斷對端是否在線。這兩種幀的發送和接收不對WEB應用公開接口,由實現WebSocket協議的底層應用(例如瀏覽器)來實現它。
 


免責聲明!

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



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