Websocket 基礎篇

xjbclz發表於2017-05-13

轉自:https://www.qcloud.com/community/article/142731?fromSource=gwzcw.116656.116656.116656


一、理解 socket , tcp , websocket, http 的聯絡和區別

socket

socket 是應用層與 TCP/IP 協議族通訊的中間軟體抽象層,它是一組介面

一個套接字介面構成一個連線的一端,而一個連線可完全由一對套接字介面規定

socket 起源於 Unix,而 Unix/Linux 基本哲學之一就是“一切皆檔案”,都可以用“開啟 open –> 讀寫 write/read –> 關閉 close”模式來操作。socket 即是一種特殊的檔案,一些 socket 函式就是對其進行的操作(讀/寫 IO、開啟、關閉)

Unix 中的 Socket,讀起來太抽象,打個具體的比方吧,我們的訊息佇列檔案就是 s 型別的檔案,就是 socket 檔案:(appplatform 框架下訊息佇列檔案存在/tmp/app 目錄下)

ps:appplatfrom 裡面配的兩個 MsgQKey 在 ServiceConfig.xml 檔案中配置的,成對出現可以在這裡查到。27e5→27e50(加個 0)→163408(再換成十進位制)。。。扯遠了。

網路中的 socekt:

在本地可以通過程式 PID 來唯一標識一個程式,但是在網路中這是行不通的。其實 TCP/IP 協議族已經幫我們解決了這個問題,網路層的“ip 地址”可以唯一標識網路中的主機,而傳輸層的“協議+埠”可以唯一標識主機中的應用程式(程式)。這樣利用三元組(ip 地址,協議,埠)就可以標識網路的程式了,網路中的程式通訊就可以利用這個標誌與其它程式進行互動。

websocket

先看網路協議圖:

就是說 Websocket 是應用層協議的一種,建立在 http 協議之上,它的誕生是為了建立一種「雙向通訊」的協議,來作為 HTTP 協議的一個替代者。

情不自禁要問,為什麼要用 WebSocket 來替代 HTTP?Http 也有 Keep-Alive,如圖隨便抓個 http 的包都能看到 Keep-Alive。

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 訊息對,仍然會造成資源的浪費、實時性不強等問題。

!

相同點

•都是基於 TCP 的應用層協議

•都使用 Request/Response 模型進行連線的建立

•在連線的建立過程中對錯誤的處理方式相同,在這個階段 WS 可能返回和 HTTP 相同的返回碼

•都可以在網路中傳輸資料

不同點

•WS 使用 HTTP 來建立連線,但是定義了一系列新的 header 域,這些域在 HTTP 中並不會使用

•WS 的連線不能通過中間人來轉發,它必須是一個直接連線

•WS 連線建立之後,通訊雙方都可以在任何時刻向另一方傳送資料

•WS 連線建立之後,資料的傳輸使用幀來傳遞,不再需要 Request 訊息

•WS 的資料幀有序

二、websocket 握手

出於相容性的考慮,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 訊息來建立直接連線。

  1. Sec-WebSocket-*:第 7 行標識了客戶端支援的子協議的列表(關於子協議會在下面介紹),第 8 行標識了客戶端支援的 WS 協議的版本列表,第 5 行用來傳送給伺服器使用(伺服器會使用此欄位組裝成另一個 key 值放在握手返回資訊裡傳送客戶端)。

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

如果伺服器接受了這個請求,可能會傳送如下這樣的返回資訊,這是一個標準的 HTTP 的 Response 訊息。101 表示伺服器收到了客戶端切換協議的請求,並且同意切換到此協議。RFC2616 規定只有 HTTP1.1 及 HHTTP1.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

ws 協議預設使用 80 埠,wss 協議預設使用 443 埠。(和 http 一樣啊:relaxed:)

收發資料幀:

客戶端和服務端都能在任意時候傳送資料,(不管是從客戶端到服務端還是相反) 每個資料幀的格式都是:

對報文的詳細解析過程可以參考文章如下:http://www.cnblogs.com/yjf512/archive/2013/02/18/2915171.html

檢視資料:

http://www.jianshu.com/p/59b5594ffbb0

http://www.jianshu.com/p/0e5b946880b4

http://www.jianshu.com/p/f666da1b1835

https://developer.mozilla.org/zh-CN/docs/WebSockets/Writing_WebSocket_servers