WebSocket
1 WebSocket概述
1)WebSocket是一種網(wǎng)絡(luò)通信協(xié)議,是HTML5開始提供的一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議;是為了兼容現(xiàn)有瀏覽器的握手規(guī)范;
2)是一種瀏覽器與服務(wù)器進(jìn)行全雙工通信的網(wǎng)絡(luò)技術(shù),屬于應(yīng)用層協(xié)議,基于TCP傳輸協(xié)議,并復(fù)用HTTP的握手通道;
3)在 WebSocketAPI中,瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道。兩者之間就直接可以數(shù)據(jù)互相傳送。
4)瀏覽器通過 JavaScript向服務(wù)器發(fā)出建立 WebSocket連接的請求,連接建立以后,客戶端和服務(wù)器端就可以通過 TCP連接直接交換數(shù)據(jù)。
5)當(dāng)獲取 WebSocket連接后,你可以通過 send() 方法來向服務(wù)器發(fā)送數(shù)據(jù),并通過 onmessage 事件來接收服務(wù)器返回的數(shù)據(jù)。
2 WebSocket與HTTP協(xié)議
1)HTTP協(xié)議只能由客戶端發(fā)起通信,這種單向請求的特點(diǎn),帶來的問題是如果服務(wù)器有連續(xù)的狀態(tài)變化,客戶端獲知非常麻煩,只能用輪詢的方式,每隔一段時(shí)間,發(fā)出一個(gè)詢問,了解服務(wù)器有沒有新的信息,這種效率比較低,浪費(fèi)資源;
2)websocket是一個(gè)持久化的協(xié)議;而HTTP是非持久化協(xié)議;
3)HTTP1.0生命周期是通過Request界定,一個(gè)Request一個(gè)Response,則請求就結(jié)束了;HTTP1.1中有一個(gè)keep-alive,在一個(gè)HTTP連接中,可以發(fā)送多個(gè)Request,接收多個(gè)Response,Response和Request對應(yīng)的,且Response是被動(dòng)的,不能主動(dòng)發(fā)起;
4)http是一個(gè)無狀態(tài)協(xié)議;

3 WebSocket特點(diǎn)
3.1 優(yōu)點(diǎn)
1)服務(wù)器可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是真正的雙向平等對話,屬于服務(wù)器推送技術(shù)的一種;
2)支持雙向通信,實(shí)時(shí)性更強(qiáng);
3)更好的二進(jìn)制支持;
4)較少的控制開銷。連接創(chuàng)建后,ws客戶端、服務(wù)端進(jìn)行數(shù)據(jù)交換時(shí),協(xié)議控制的數(shù)據(jù)包頭部較小。在不包含頭部的情況下,服務(wù)端到客戶端的包頭只有2~10字節(jié)(取決于數(shù)據(jù)長度),客戶端到服務(wù)端的話,需要加上額外的4字節(jié)的掩碼。而HTTP協(xié)議每次通信都需要攜帶完整的頭部。
5)支持?jǐn)U展。ws協(xié)議定義了擴(kuò)展,用戶可以擴(kuò)展協(xié)議,或者事項(xiàng)自定義的子協(xié)議。(比如支持自定義壓縮算法等)
3.2 舉例
客戶端發(fā)起:

服務(wù)端回復(fù):服務(wù)端返回內(nèi)容如下,狀態(tài)代碼101表示協(xié)議切換。到此完成協(xié)議升級,后續(xù)的數(shù)據(jù)交互都按照新的協(xié)議來

? Connection: Upgrade:表示要升級協(xié)議
? Upgrade: websocket:表示要升級到websocket協(xié)議。
? Sec-WebSocket-Version: 13:表示websocket的版本。如果服務(wù)端不支持該版本,需要返回一個(gè)Sec-WebSocket-Versionheader,里面包含服務(wù)端支持的版本號。
? Sec-WebSocket-Key:與后面服務(wù)端響應(yīng)首部的Sec-WebSocket-Accept是配套的,提供基本的防護(hù),比如惡意的連接,或者無意的連接。
4 WebSocket原理
4.1 其他方式的被動(dòng)性
1)ajax輪詢:讓瀏覽器每隔幾秒就發(fā)送一次請求,詢問服務(wù)器是否有信息;

2)long poll:類似于ajax輪詢,采取阻塞模型(一直打電話,沒收到對方回應(yīng)就不掛電話),客戶端發(fā)起連接后,如果沒信息,就一直不返回Response給客戶端,直到有消息才返回,然后客戶端再次建立連接;

3)websocket:只需要經(jīng)過一次HTTP請求,就可以不斷的互通消息;

4.2 websocket的產(chǎn)生
1)ajax輪詢需要服務(wù)器有很快的處理速度和資源;long poll需要有很高的并發(fā),及同時(shí)接待客戶的能力;
2)websocket在服務(wù)器完成協(xié)議升級后(HTTP->websocket),服務(wù)端就可以主動(dòng)推送消息給客戶端;
3)只需要經(jīng)過一次HTTP請求,就可以不斷的互通消息;解決了服務(wù)器消耗資源以及同步延遲問題;
5 常用屬性
5.1 @WebSocketEndpoint
注解是一個(gè)類層次的注解,它的功能主要是將目前的類定義成一個(gè)websocket服務(wù)器端。注解的值將被用于監(jiān)聽用戶連接的終端訪問URL地址。
5.2 @onOpen
打開一個(gè)新連接,即有新連接時(shí),會調(diào)用被此注解的方法。
5.3 @onClose
關(guān)閉連接時(shí)調(diào)用。
5.4 @onMessage
當(dāng)服務(wù)器接收到客戶端發(fā)送的消息時(shí)所調(diào)用的方法。
5.5 @PathParam
接收 uri參數(shù)的,與@PathVariable功能差不多,可通過url獲取對應(yīng)值
6 WebSocket 后端JAVA實(shí)現(xiàn)
6.1 pom文件依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

6.2 運(yùn)行類
1)添加注解:
@EnableWebSocket
2)添加Bean:
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}

6.3 WebSocket controller類
1)注解:
@ServerEndpoint(value = "/heartbeatMonitor/{userId}")
@Component
2)其他類注入方式:

3)方法使用:
以@ServerEndpoint(value = "/websocket/{usernick}")為例
1. @OnOpen
2. public void onOpen(@PathParam(value = "usernick") String userNick,Session session) {
3. String message = "有新游客[" + userNick + "]加入聊天室!";
4. log.info(message);
5. WebSocketUtil.addSession(userNick, session);
6. //此時(shí)可向所有的在線通知 某某某登錄了聊天室
7. WebSocketUtil.sendMessageForAll(message);
8. }
10. @OnClose
11. public void onClose(@PathParam(value = "usernick") String userNick,Session session) {
12. String message = "游客[" + userNick + "]退出聊天室!";
13. log.info(message);
14. WebSocketUtil.remoteSession(userNick);
15. //此時(shí)可向所有的在線通知 某某某登錄了聊天室
16. WebSocketUtil.sendMessageForAll(message);
17. }
19. @OnMessage
20. public void OnMessage(@PathParam(value = "usernick") String userNick, String message) {
21. //類似群發(fā)
22. String info = "游客[" + userNick + "]:" + message;
23. log.info(info);
24. WebSocketUtil.sendMessageForAll(message);
25. }
27. @OnError
28. public void onError(Session session, Throwable throwable) {
29. log.error("異常:", throwable);
30. try {
31. session.close();
32. } catch (IOException e) {
33. e.printStackTrace();
34. }
35. throwable.printStackTrace();
36. }

6.4 測試
http://coolaf.com/tool/chattest
1)連接

2)發(fā)送消息
