tags:WebSocket和Socketjavascript
引言:好多朋友想知道WebSocket和Socket的聯繫和區別,下面就是大家想要的php
先來一張以前收集的圖,我看到這張圖真的是笑了,當時還給我朋友門轉發了一下,不知道你笑了沒有。
html
看完上面的圖,應該猜到了,他們之間也確實沒有什麼實質性的聯繫,固然除了名字有點相同,文章後面有名稱的由來能夠參考閱讀java
英文socket的意思是插座,網絡中的Socket是一個抽象的接口,能夠理解爲網絡中鏈接的兩端。一般被叫作套接字接口,其意義在對傳輸層進行封裝屏蔽了傳輸層的複雜性。它並非一個協議,是爲了你們更方便的使用傳輸層協議產生的一個抽象層。大部分的主流編程語言都提供socket函數。咱們拿php來舉例說明nginx
<?php 1. socket_create() - 建立一個套接字 2. socket_accept() - Accepts a connection on a socket (接收) 3. socket_bind() - 給套接字綁定IP和端口號 4. socket_connect() - 和一個套接字創建鏈接 5. socket_listen() - Listens for a connection on a socket (監聽) 6. socket_last_error() - Returns the last error on the socket 7. socket_strerror() - Return a string describing a socket error ?>
咱們能夠用這些函數來創建鏈接實現通訊功能。關於Socket咱們就說這些web
說道WebSocket瞭解過一些的人可能會以爲有些高大上的感受,它的誕生還有些故事能夠講,大概是在爲w3c放棄了html後,還有那麼一羣人不服氣(不想放棄),想要繼續推進html發展,同時他們也發展了一些其餘的網絡標準,而且被官方接收。而WebSocket就是其中一種,是爲了建立一種雙向通訊(全雙工)的協議,來做爲HTTP協議的一個替代者,以解決基於http上的長輪詢等技術解決不了(或者解決的不那麼優美)的問題。並且這廝一開始並不叫WebSocket,好像是叫webConnect之類的,最後是一位工程師提議說要麼我們叫WebSocket吧,而後。。。。。,好了故事就這樣,他既然是HTTP的替代者,咱們首先看一下它和HTTP(或者HTTP的長鏈接)的聯繫和區別。ajax
首先二者都是應用層協議,並且 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鏈接始於握手(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
上面是報文區別,還有一些其餘的特性
因爲展開來說的話篇幅太長,你們也能夠自行深刻了解。
在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 ... | +---------------------------------------------------------------+
參考上面的數據幀格式來講一下各個字段的含義,重要字段加粗
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)