Websockethtml
websocket爲一次HTTP握手後,後續通信爲tcp協議的通信方式。前端
固然,和HTTP同樣,websocket也有一些約定的通信方式,http通信方式爲http開頭的方式,e.g. http://xxx.com/path ,websocket通信方式則爲ws開頭的方式,e.g. ws://xxx.com/pathhtml5
SSL:node
下面的是Netty中Websocket的邏輯圖:web
WebSocket是HTML5出的東西(協議),也就是說HTTP協議沒有變化,或者說不要緊,但HTTP是不支持持久鏈接的(長鏈接,循環鏈接的不算)跨域
首先HTTP有 1.1
和 1.0
之說,也就是所謂的 keep-alive
,把多個HTTP請求合併爲一個,可是 Websocket
實際上是一個新協議,跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已,也就是說它是HTTP協議上的一種補充能夠經過這樣一張圖理解瀏覽器
有交集,可是並非所有。緩存
共同點是:都是基於TCP協議進行client-server的連接,websocket是HTML5提出的一套補缺HTTP連接中不能持久連接的特色(除長鏈接,長輪詢)。安全
回顧下下面幾個概念:服務器
輪詢與長輪詢共同的缺點是:須要浪費不少的http資源,請求的數量較大。
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 支持事件觸發爲基礎進行的雙向數據通訊。
一樣做爲應用層的協議,WebSocket在現代的軟件開發中被愈來愈多的實踐,和HTTP有不少類似的地方,這裏將它們簡單的作一個純我的、非權威的比較:
HTTP1.1的鏈接默認使用持續鏈接(persistent connection),持續鏈接指的是,有時是客戶端會須要在短期內向服務端請求大量的相關的資源,若是不是持續鏈接,那麼每一個資源都要創建一個新的鏈接,HTTP底層使用的是TCP,那麼每次都要使用三次握手創建TCP鏈接,將形成極大的資源浪費。
持續鏈接能夠帶來不少的好處:
HTTP1.1的服務器使用TCP的流量控制來控制HTTP的流量,HTTP1.1的客戶端在收到服務器鏈接中發過來的error信息,就要立刻關閉此連接。關於HTTP鏈接還有不少細節,以後再詳述。
短答案
就像Java和JavaScript,並無什麼太大的關係,但又不能說徹底不要緊。能夠這麼說:
長答案
當咱們探討兩件事物的區別和聯繫時,咱們想探討些什麼?
對於我來講,大多數狀況是想知道兩件事物自己,而並非想只想瞭解「區別」自己。那麼對這個問題最直接的解決方法應該是去了解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,看工做的公司就知道這我的的背景了。
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); };
接口的內容能夠分爲三類:狀態變量、網絡功能和消息處理等。
內容的肯定
大多數新技術的出現都是創建在已有技術的鋪墊之上的,WebSocket內容的肯定也是如此,其中就有Comet看不到的貢獻,Comet是一個頗有趣的技術,有興趣能夠看看這裏
結論
能夠把WebSocket想象成HTTP,HTTP和Socket什麼關係,WebSocket和Socket就是什麼關係。
只從RFC發佈的時間看來,WebSocket要晚近不少,HTTP 1.1是1999年,WebSocket則是12年以後了。WebSocket協議的開篇就說,本協議的目的是爲了解決基於瀏覽器的程序須要拉取資源時必須發起多個HTTP請求和長時間的輪訓的問題而建立的。
WebSocket協議還很年輕,RFC文檔相比HTTP的發佈時間也很短,它的誕生是爲了建立一種「雙向通訊」的協議,來做爲HTTP協議的一個替代者。那麼首先看一下它和HTTP(或者HTTP的長鏈接)的區別。
上一篇中提到WebSocket的目的就是解決網絡傳輸中的雙向通訊的問題,HTTP1.1默認使用持久鏈接(persistent connection),在一個TCP鏈接上也能夠傳輸多個Request/Response消息對,可是HTTP的基本模型仍是一個Request對應一個Response。這在雙向通訊(客戶端要向服務器傳送數據,同時服務器也須要實時的向客戶端傳送信息,一個聊天系統就是典型的雙向通訊)時通常會使用這樣幾種解決方案:
websocket缺點:
少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別。
WebSocket的目的是取代HTTP在雙向通訊場景下的使用,並且它的實現方式有些也是基於HTTP的(WS的默認端口是80
和443
)。現有的網絡環境(客戶端、服務器、網絡中間人、代理等)對HTTP都有很好的支持,因此這樣作能夠充分利用現有的HTTP的基礎設施,有點向下兼容的意味。
簡單來說,WS協議有兩部分組成:握手和數據傳輸。
出於兼容性的考慮,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域。
Upgrade:upgrade是HTTP1.1中用於定義轉換協議的header域。它表示,若是服務器支持的話,客戶端但願使用現有的「網絡層」已經創建好的這個「鏈接(此處是TCP鏈接)」,切換到另一個「應用層」(此處是WebSocket)協議。
Connection:HTTP1.1中規定Upgrade只能應用在「直接鏈接」中,因此帶有Upgrade頭的HTTP1.1消息必須含有Connection頭,由於Connection頭的意義就是,任何接收到此消息的人(每每是代理服務器)都要在轉發此消息以前處理掉Connection中指定的域(不轉發Upgrade域)。
若是客戶端和服務器之間是經過代理鏈接的,那麼在發送這個握手消息以前首先要發送CONNECT消息來創建直接鏈接。
首先, Sec-WebSocket-Key
是一個 Base64 encode
的值,這個是瀏覽器隨機生成的,發送給服務器使用(服務器會使用此字段組裝成另外一個key值放在握手返回信息裏發送客戶端)。用於驗證服務端是否是真的是Websocket代理。
而後, Sec_WebSocket-Protocol
是一個用戶定義的字符串,標識了客戶端支持的子協議的列表。用來區分同URL下,不一樣的服務所須要的協議。
最後, Sec-WebSocket-Version
是告訴服務器所使用的 Websocket Draft
(協議版本),在最初的時候,Websocket協議還在 Draft
階段,各類奇奇怪怪的協議都有,並且還有不少期奇奇怪怪不一樣的東西,什麼Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多但是一個大難題。
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行,則是表示最終使用的協議。
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狀態(就是正在創建鏈接)的鏈接。從創建鏈接到發送握手消息這個過程大體是這樣的:
若是客戶端處於一個代理環境中,它首先要請求它的代理來創建一個到達目標地址的TCP鏈接。
例如,若是客戶端處於代理環境中,它想要鏈接某目標地址的80
端口,它可能要收現發送如下消息:
CONNECT example.com:80 HTTP/1.1
Host: example.com
若是客戶端沒有處於代理環境中,它就要首先創建一個到達目標地址的直接的TCP鏈接。
ws://example.com/chat GET /chat HTTP/1.1
101
,則按照RFC2616進行處理。若是是101
,進行下一步,開始解析header域,全部header域的值不區分大小寫。服務端指的是全部參與處理WebSocket消息的基礎設施,好比若是某服務器使用Nginx(A)來處理WebSocket,而後把處理後的消息傳給響應的服務器(B),那麼A和B都是這裏要討論的服務端的範疇。
若是請求是HTTPS,則首先要使用TLS進行握手,若是失敗,則關閉鏈接,若是成功,則以後的數據都經過此通道進行發送。
以後服務端能夠進行一些客戶端驗證步驟(包括對客戶端header域的驗證),若是須要,則按照RFC2616來進行錯誤碼的返回。
若是一切都成功,則返回成功的Response握手消息。
此握手消息是一個標準的HTTP Response消息,同時它包含了如下幾個部分:
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
(一個UUID)。一旦這個握手發出去,服務端就認爲此WebSocket鏈接已經創建成功,處於OPEN狀態。它就能夠開始發送數據了。
Sec-WebSocket-Version能夠被通訊雙方用來支持更多的協議的擴展,RFC6455中定義的值爲13
,WebSocket的客戶端和服務端可能回自定義更多的版本號來支持更多的功能。其使用方法如上文所述。
WebSocket中全部發送的數據使用幀的形式發送。客戶端發送的數據幀都要通過掩碼處理,服務端發送的全部數據幀都不能通過掩碼處理。不然對方須要發送關閉幀。
一個幀包含一個幀類型的標識碼,一個負載長度,和負載。負載包括擴展內容和應用內容。
幀類型是由一個4位長的叫Opcode的值表示,任何WebSocket的通訊方收到一個位置的幀類型,都要以鏈接失敗的方式斷開此鏈接。
RFC6455中定義的幀類型以下所示:
Opcode == 0 繼續
表示此幀是一個繼續幀,須要拼接在上一個收到的幀以後,來組成一個完整的消息。因爲這種解析特性,非控制幀的發送和接收必須是相同的順序。
目前有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):