一、WebSocket是HTML5中的協議,支持持久連接;而Http協議不支持持久連接。
首先HTMl5指的是一系列新的API,或者說新規范,新技術。WebSocket是HTML5中新協議、新API.
Http協議本身只有1.0和1.1,也就是所謂的Keep-alive,把多個Http請求合並為一個。
二、WebSocket是什么樣的協議,具體有什么優點。
首先,相對於Http這種非持久的協議來說,WebSocket是一種持久化的協議。
舉例說明:
(1)Http的生命周期通過Request來界定,也就是Request一個Response,那么在Http1.0協議中,這次Http請求就結束了。
在Http1.1中進行了改進,是的有一個Keep-alive,也就是說,在一個Http連接中,可以發送多個Request,接收多個Response。
但是必須記住,在Http中一個Request只能對應有一個Response,而且這個Response是被動的,不能主動發起。
(2)WebSocket是基於Http協議的,或者說借用了Http協議來完成一部分握手,在握手階段與Http是相同的。
首先我們來看個典型的Websocket握手(借用Wikipedia的。。)
熟悉HTTP的童鞋可能發現了,這段類似HTTP協議的握手請求中,多了幾個東西。
我會順便講解下作用。
這個就是Websocket的核心了,告訴Apache、Nginx等服務器:
注意啦,窩發起的是Websocket協議,快點幫我找到對應的助理處理~不是那個老土的HTTP。
首先,Sec-WebSocket-Key 是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:
泥煤,不要忽悠窩,我要驗證尼是不是真的是Websocket助理。
然后,Sec_WebSocket-Protocol 是一個用戶定義的字符串,用來區分同URL下,不同的服務所需要的協議。簡單理解: 今晚我要服務A,別搞錯啦~
最后,Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各種奇奇怪怪的協議都有,而且還有很多期奇奇怪怪不同的東西,什么Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多可是一個大難題。。不過現在還好,已經定下來啦~大家都使用的一個東西~ 脫水: 服務員,我要的是13歲的噢→_→
然后服務器會返回下列東西,表示已經接受到請求, 成功建立Websocket啦!
這里開始就是HTTP最后負責的區域了,告訴客戶,我已經成功切換協議啦~
依然是固定的,告訴客戶端即將升級的是Websocket協議,而不是mozillasocket,lurnarsocket或者shitsocket。
然后,Sec-WebSocket-Accept 這個則是經過服務器確認,並且加密過后的 Sec-WebSocket-Key。服務器: 好啦好啦,知道啦,給你看我的ID CARD來證明行了吧。。
后面的,Sec-WebSocket-Protocol 則是表示最終使用的協議。
至此,HTTP已經完成它所有工作了,接下來就是完全按照Websocket協議進行了。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
我會順便講解下作用。
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
然后,Sec_WebSocket-Protocol 是一個用戶定義的字符串,用來區分同URL下,不同的服務所需要的協議。簡單理解: 今晚我要服務A,別搞錯啦~
最后,Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各種奇奇怪怪的協議都有,而且還有很多期奇奇怪怪不同的東西,什么Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多可是一個大難題。。不過現在還好,已經定下來啦~大家都使用的一個東西~ 脫水: 服務員,我要的是13歲的噢→_→
然后服務器會返回下列東西,表示已經接受到請求, 成功建立Websocket啦!
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Upgrade: websocket
Connection: Upgrade
然后,Sec-WebSocket-Accept 這個則是經過服務器確認,並且加密過后的 Sec-WebSocket-Key。服務器: 好啦好啦,知道啦,給你看我的ID CARD來證明行了吧。。
后面的,Sec-WebSocket-Protocol 則是表示最終使用的協議。
至此,HTTP已經完成它所有工作了,接下來就是完全按照Websocket協議進行了。
其后是WebSocket協議的工作。
三、Long poll 和Ajax輪詢以及WebSocket的原理
1、Ajax輪詢的原理。
場景如下:
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有。。(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:你好煩啊,沒有啊。。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:好啦好啦,有啦給你。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:。。。。。沒。。。。沒。。。沒有(Response) ---- loop
服務端:沒有(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有。。(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:你好煩啊,沒有啊。。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:好啦好啦,有啦給你。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:。。。。。沒。。。。沒。。。沒有(Response) ---- loop
2、long poll的原理。
long poll 其實原理跟 ajax輪詢 差不多,都是采用輪詢的方式,不過采取的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起連接后,如果沒消息,就一直不返回Response給客戶端。直到有消息才返回,返回完之后,客戶端再次建立連接,周而復始。
場景如下:
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request)
服務端:額。。 (等待到有消息的時候)。。來 給你(Response)
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request) -loop
場景如下:
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request)
服務端:額。。 (等待到有消息的時候)。。來 給你(Response)
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request) -loop
從上面可以看出其實這兩種方式,都是在不斷地建立HTTP連接,然后等待服務端處理,可以體現HTTP協議的另外一個特點,
被動性。
何為被動性呢,其實就是,服務端不能主動聯系客戶端,只能有客戶端發起。
簡單地說就是,服務器是一個很懶的冰箱(這是個梗)(不會、不能主動發起連接),但是上司有命令,如果有客戶來,不管多么累都要好好接待。
何為被動性呢,其實就是,服務端不能主動聯系客戶端,只能有客戶端發起。
簡單地說就是,服務器是一個很懶的冰箱(這是個梗)(不會、不能主動發起連接),但是上司有命令,如果有客戶來,不管多么累都要好好接待。
說完這個,我們再來說一說上面的缺陷
從上面很容易看出來,不管怎么樣,上面這兩種都是非常消耗資源的。
ajax輪詢 需要服務器有很快的處理速度和資源。(速度)
long poll 需要有很高的並發,也就是說同時接待客戶的能力。(場地大小)
所以ajax輪詢 和long poll 都有可能發生這種情況。
客戶端:啦啦啦啦,有新信息么?
服務端:月線正忙,請稍后再試(503 Server Unavailable)
客戶端:。。。。好吧,啦啦啦,有新信息么?
服務端:月線正忙,請稍后再試(503 Server Unavailable)
客戶端:
然后服務端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。
從上面很容易看出來,不管怎么樣,上面這兩種都是非常消耗資源的。
ajax輪詢 需要服務器有很快的處理速度和資源。(速度)
long poll 需要有很高的並發,也就是說同時接待客戶的能力。(場地大小)
所以ajax輪詢 和long poll 都有可能發生這種情況。
客戶端:啦啦啦啦,有新信息么?
服務端:月線正忙,請稍后再試(503 Server Unavailable)
客戶端:。。。。好吧,啦啦啦,有新信息么?
服務端:月線正忙,請稍后再試(503 Server Unavailable)
客戶端:

然后服務端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。
3、WebSocket的原理。
通過上面這個例子,我們可以看出,這兩種方式都不是最好的方式,需要很多資源。
一種需要更快的速度,一種需要更多的'電話'。這兩種都會導致'電話'的需求越來越高。
哦對了,忘記說了HTTP還是一個狀態協議。
通俗的說就是,服務器因為每天要接待太多客戶了,是個 健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。
所以在這種情況下出現了,Websocket出現了。
他解決了HTTP的這幾個難題。
首先, 被動性,當服務器完成協議升級后(HTTP->Websocket),服務端就可以主動推送信息給客戶端啦。
所以上面的情景可以做如下修改。
客戶端:啦啦啦,我要建立Websocket協議,需要的服務:chat,Websocket協議版本:17(HTTP Request)
服務端:ok,確認,已升級為Websocket協議(HTTP Protocols Switched)
客戶端:麻煩你有信息的時候推送給我噢。。
服務端:ok,有的時候會告訴你的。
服務端:balabalabalabala
服務端:balabalabalabala
服務端:哈哈哈哈哈啊哈哈哈哈
服務端:笑死我了哈哈哈哈哈哈哈
就變成了這樣,只需要經過 一次HTTP請求,就可以做到源源不斷的信息傳送了。(在程序設計中,這種設計叫做回調,即:你有信息了再來通知我,而不是我傻乎乎的每次跑來問你)
這樣的協議解決了上面同步有延遲,而且還非常消耗資源的這種情況。
那么為什么他會解決服務器上消耗資源的問題呢?
其實我們所用的程序是要經過兩層代理的,即 HTTP協議在Nginx等服務器的解析下,然后再傳送給相應的 Handler(PHP等)來處理。
簡單地說,我們有一個非常快速的接 線員(Nginx),他負責把問題轉交給相應的 客服(Handler)。
本身 接線員基本上速度是足夠的,但是每次都卡在 客服(Handler)了,老有 客服處理速度太慢。,導致客服不夠。
Websocket就解決了這樣一個難題,建立后,可以直接跟接線員建立持 久連接,有信息的時候客服想辦法通知接線員,然后 接線員在統一轉交給客戶。
這樣就可以解決客服處理速度過慢的問題了。
同時,在傳統的方式上,要不斷的建立,關閉HTTP協議,由於HTTP是非狀態性的,每次都要 重新傳輸identity info(鑒別信息),來告訴服務端你是誰。
雖然接線員很快速,但是每次都要聽這么一堆,效率也會有所下降的,同時還得不斷把這些信息轉交給客服,不但浪費客服的 處理時間,而且還會在網路傳輸中消耗 過多的流量/時間。
但是Websocket只需要 一次HTTP握手,所以說整個通訊過程是建立在一次連接/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的信息,直到你關閉請求,這樣就解決了接線員要反復解析HTTP協議,還要查看identity info的信息。
同時由 客戶主動詢問,轉換為 服務器(推送)有信息的時候就發送(當然客戶端還是等主動發送信息過來的。。),沒有信息的時候就交給接線員(Nginx),不需要占用本身速度就慢的 客服(Handler)了
--------------------
至於怎么在不支持Websocket的客戶端上使用Websocket。。答案是: 不能
但是可以通過上面說的 long poll 和 ajax 輪詢來 模擬出類似的效果
一種需要更快的速度,一種需要更多的'電話'。這兩種都會導致'電話'的需求越來越高。
哦對了,忘記說了HTTP還是一個狀態協議。
通俗的說就是,服務器因為每天要接待太多客戶了,是個 健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。
所以在這種情況下出現了,Websocket出現了。
他解決了HTTP的這幾個難題。
首先, 被動性,當服務器完成協議升級后(HTTP->Websocket),服務端就可以主動推送信息給客戶端啦。
所以上面的情景可以做如下修改。
客戶端:啦啦啦,我要建立Websocket協議,需要的服務:chat,Websocket協議版本:17(HTTP Request)
服務端:ok,確認,已升級為Websocket協議(HTTP Protocols Switched)
客戶端:麻煩你有信息的時候推送給我噢。。
服務端:ok,有的時候會告訴你的。
服務端:balabalabalabala
服務端:balabalabalabala
服務端:哈哈哈哈哈啊哈哈哈哈
服務端:笑死我了哈哈哈哈哈哈哈
就變成了這樣,只需要經過 一次HTTP請求,就可以做到源源不斷的信息傳送了。(在程序設計中,這種設計叫做回調,即:你有信息了再來通知我,而不是我傻乎乎的每次跑來問你)
這樣的協議解決了上面同步有延遲,而且還非常消耗資源的這種情況。
那么為什么他會解決服務器上消耗資源的問題呢?
其實我們所用的程序是要經過兩層代理的,即 HTTP協議在Nginx等服務器的解析下,然后再傳送給相應的 Handler(PHP等)來處理。
簡單地說,我們有一個非常快速的接 線員(Nginx),他負責把問題轉交給相應的 客服(Handler)。
本身 接線員基本上速度是足夠的,但是每次都卡在 客服(Handler)了,老有 客服處理速度太慢。,導致客服不夠。
Websocket就解決了這樣一個難題,建立后,可以直接跟接線員建立持 久連接,有信息的時候客服想辦法通知接線員,然后 接線員在統一轉交給客戶。
這樣就可以解決客服處理速度過慢的問題了。
同時,在傳統的方式上,要不斷的建立,關閉HTTP協議,由於HTTP是非狀態性的,每次都要 重新傳輸identity info(鑒別信息),來告訴服務端你是誰。
雖然接線員很快速,但是每次都要聽這么一堆,效率也會有所下降的,同時還得不斷把這些信息轉交給客服,不但浪費客服的 處理時間,而且還會在網路傳輸中消耗 過多的流量/時間。
但是Websocket只需要 一次HTTP握手,所以說整個通訊過程是建立在一次連接/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的信息,直到你關閉請求,這樣就解決了接線員要反復解析HTTP協議,還要查看identity info的信息。
同時由 客戶主動詢問,轉換為 服務器(推送)有信息的時候就發送(當然客戶端還是等主動發送信息過來的。。),沒有信息的時候就交給接線員(Nginx),不需要占用本身速度就慢的 客服(Handler)了
--------------------
至於怎么在不支持Websocket的客戶端上使用Websocket。。答案是: 不能
但是可以通過上面說的 long poll 和 ajax 輪詢來 模擬出類似的效果
二:如何測試websocket連接成功,我們可以通過console控制台一個方法來實現如下

#我們可以使用方法 new websocket(url,protocols) 來發送請求
#這里的第一個url參數是必寫協議默認使用的websocket

#ws 和 wss 這里是由區別的,先留着疑問,后面會介紹?
三 ws 和 wss 又是什么鬼,它們區別在哪里?
Websocket使用 ws 或 wss 的統一資源標志符,類似於 HTTP 或 HTTPS ,其中 wss 表示在 TLS 之上的 Websocket ,相當於 HTTPS 了。如: ws://example.com/chat wss://example.com/chat 默認情況下,Websocket 的 ws 協議使用 80 端口;運行在TLS之上時,wss 協議默認使用 443 端口。其實說白了,wss 就是 ws 基於 SSL 的安全傳輸,與 HTTPS 一樣樣的道理。 如果你的網站是 HTTPS 協議的,那你就不能使用 ws:// 了,瀏覽器會 block 掉連接,和 HTTPS 下不允許 HTTP 請求一樣,如下圖: Mixed Content: The page at 'https://domain.com/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://x.x.x.x:xxxx/'. This request has been blocked; this endpoint must be available over WSS. 這種情況,毫無疑問我們就需要使用 wss:\ 安全協議了,我們是不是簡單的把 ws:\ 改為 wss:\ 就行了?那試試唄。 改好了,報錯啦!!! VM512:35 WebSocket connection to 'wss://IP地址:端口號/websocket' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR 很明顯 SSL 協議錯誤,說明就是證書問題了。記着,這時候我們一直拿的是 IP地址 + 端口號 這種方式連接 WebSocket 的,這根本就沒有證書存在好么,況且生成環境你也要用 IP地址 + 端口號 這種方式連接 WebSocket 嗎?肯定不行阿,要用域名方式連接 WebSocket 阿。
Nginx 配置域名支持 WSS 不用廢話,直接在配置 HTTPS 域名位置加入如下配置: location /websocket { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";} 接着拿域名再次連接試一下,不出意外會看 101 狀態碼: 這樣就完成了,在 HTTPPS 下以域名方式連接 WebSocket ,可以開心的玩耍了。 稍微解釋一下 Nginx 配置 Nginx 自從 1.3 版本就開始支持 WebSocket 了,並且可以為 WebSocket 應用程序做反向代理和負載均衡。 WebSocket 和 HTTP 協議不同,但是 WebSocket 中的握手和 HTTP 中的握手兼容,它使用 HTTP 中的 Upgrade 協議頭將連接從 HTTP 升級到 WebSocket,當客戶端發過來一個 Connection: Upgrade 請求頭時,Nginx 是不知道的,所以,當 Nginx 代理服務器攔截到一個客戶端發來的 Upgrade 請求時,需要顯式來設置 Connection 、 Upgrade 頭信息,並使用 101(交換協議)返回響應,在客戶端和代理服務器、后端服務器之間建立隧道來支持 WebSocket。 當然,還需要注意一下,WebSockets 仍然受到 Nginx 缺省為60秒的 proxy_read_timeout 的影響。這意味着,如果你有一個程序使用了 WebSockets,但又可能超過60秒不發送任何數據的話,那你要么需要增加超時時間,要么實現一個 ping 的消息以保持聯系。使用 ping 的解決方法有額外的好處,可以發現連接是否被意外關閉。 更具體文檔詳見 Nginx 官方文檔: http://nginx.org/en/docs/http/websocket.html