webSocket原理探索

來源:互聯網
上載者:User

本文概述

Web Sockets的目標是在一個單獨的持久串連上提供全雙工系統、雙向通訊。在Javascript建立了Web Socket之後,會有一個HTTP請求發送到瀏覽器以發起串連。在取得伺服器響應後,建立的串連會將HTTP升級從HTTP協議交換為WebSocket協議。
由於WebSocket使用自訂的協議,所以URL模式也略有不同。未加密的串連不再是http://,而是ws://;加密的串連也不是https://,而是wss://。在使用WebSocket URL時,必須帶著這個模式,因為將來還有可能支援其他的模式。
使用自訂協議而非HTTP協議的好處是,能夠在用戶端和伺服器之間發送非常少量的資料,而不必擔心HTTP那樣位元組級的開銷。由於傳遞的資料包很小,所以WebSocket非常適合行動裝置 App。
上文中只是對Web Sockets進行了籠統的描述,接下來的篇幅會對Web Sockets的細節實現進行深入的探索,本文不會涉及到大量的程式碼片段,但是會對相關的API和技術原理進行分析,相信大家讀完下文之後再來看這段描述,會有一種豁然開朗的感覺。

一、WebSocket複用了HTTP的握手通道

“握手通道”是HTTP協議中用戶端和服務端通過"TCP三向交握"建立的串連通道。用戶端和服務端使用HTTP協議進行的每次互動都需要先建立這樣一條“通道”,然後通過這條通道進行通訊。我們熟悉的ajax互動就是在這樣一個通道上完成資料轉送的,下面是HTTP協議中建立“握手通道”的過程:

上文中我們提到:在Javascript建立了WebSocket之後,會有一個HTTP請求發送到瀏覽器以發起串連,然後服務端響應,這就是“握手“的過程,在這個握手的過程當中,用戶端和服務端主要做了兩件事情:

  1. 建立了一條串連“握手通道”用於通訊(這點和HTTP協議相同,不同的是HTTP協議完成資料互動後就釋放了這條握手通道,這就是所謂的“短串連”,它的生命週期是一次資料互動的時間,通常是毫秒層級的。)
  2. 將HTTP協議升級到WebSocket協議,並複用HTTP協議的握手通道,從而建立一條持久串連。

    說到這裡可能有人會問:HTTP協議為什麼不複用自己的“握手通道”,而非要在每次進行資料互動的時候都通過TCP三向交握重建立立“握手通道”呢?答案是這樣的:雖然“長串連”在用戶端和服務端互動的過程中省去了每次都建立“握手通道”的麻煩步驟,但是維持這樣一條“長串連”是需要消耗伺服器資源的,而在大多數情況下,這種資源的消耗又是不必要的,可以說HTTP標準的制定經過了深思熟慮的考量。到我們後邊說到WebSocket協議資料幀時,大家可能就會明白,維持一條“持久串連”服務端和用戶端需要做的事情太多了。

    說完了握手通道,我們再來看HTTP協議如何升級到WebSocket協議的。

二、HTTP協議升級為WebSocket協議

升級協議需要用戶端和服務端交流,服務端怎麼知道要將HTTP協議升級到WebSocket協議呢?它一定是接收到了用戶端發送過來的某種訊號。下面是我從Google瀏覽器中截取的“用戶端發起協議升級請求的報文”,通過分析這段報文,我們能夠得到有關WebSocket中協議升級的更多細節。

首先,用戶端發起協議升級請求。採用的是標準的HTTP報文格式,且只支援GET方法。下面是重點請求的首部的意義:
  1. Connection:Upgrade:表示要升級的協議
  2. Upgrade: websocket:表示要升級到websocket協議
  3. Sec-WebSocket-Version: 13:表示websocket的版本
  4. Sec-WebSocket-Key:UdTUf90CC561cQXn4n5XRg== :與Response Header中的響應首部Sec-WebSocket-Accept: GZk41FJZSYY0CmsrZPGpUGRQzkY=是配套的,提供基本的防護,比如惡意的串連或者無意的串連。

    其中Connection就是我們前邊提到的,用戶端發送給服務端的訊號,服務端接受到訊號之後,才會對HTTP協議進行升級。那麼服務端怎樣確認用戶端發送過來的請求是否是合法的呢?在用戶端每次發起協議升級請求的時候都會產生一個唯一碼:Sec-WebSocket-Key。服務端拿到這個碼後,通過一個演算法進行校正,然後通過Sec-WebSocket-Accept響應給用戶端,用戶端再對Sec-WebSocket-Accept進行校正來完成驗證。這個演算法很簡單:

1.將Sec-WebSocket-Key跟全域唯一的(GUID,[RFC4122])標識:258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接

2.通過SHA1計算出摘要,並轉成base64字串

258EAFA5-E914-47DA-95CA-C5AB0DC85B11這個字串又叫“魔串",至於為什麼要使用它作為Websocket握手計算中使用的字串,這點我們無需關心,只需要知道它是RFC標準規定就可以了,官方的解析也只是簡單的說此值不大可能被不明白WebSocket協議的網路終端使用。我們還是用世界上最好的語言來描述一下這個演算法吧。

public function dohandshake($sock, $data, $key) {        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $data, $match)) {            $response = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));            $upgrade  = "HTTP/1.1 101 Switching Protocol\r\n" .                "Upgrade: websocket\r\n" .                "Connection: Upgrade\r\n" .                "Sec-WebSocket-Accept: " . $response . "\r\n\r\n";            socket_write($sock, $upgrade, strlen($upgrade));            $this->isHand[$key] = true;        }    }
服務端響應用戶端的頭部資訊和HTTP協議的格式是相同的,所以這裡Sec-WebSocket-Accept欄位後邊的兩個分行符號是少不了的,這和我們使用curl工具類比get請求是一個道理。這樣展示結果似乎不太直觀,我們使用命令列CLI來根據中的Sec-WebSocket-Key和握手演算法來計算一下服務端返回的Sec-WebSocket-Accept是否正確:

可以看到,通過演算法算出來的base64字串和Sec-WebSocket-Accept是一樣的。那麼假如服務端在握手的過程中返回一個錯誤的Sec-WebSocket-Accept字串會怎麼樣呢?當然是用戶端會報錯,串連會建立失敗,大家最好嘗試一下,例如將通用唯一識別碼258EAFA5-E914-47DA-95CA-C5AB0DC85B11改為258EAFA5-E914-47DA-95CA-C5AB0DC85B12。

三、WebSocket的資料幀

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.