WebSocket,幹什麼用的?我們有了HTTP,為什麼還要用WebSocket?很多同學都會有這樣的疑問。我們先來看一個場景,大家的手機裡都有微信,在微信中,隻要有新的消息,這個聯系人的前面就會有一個紅點,這個需求要怎麼實作呢?大家思考3秒鐘。哈哈,最簡單,最笨的方法就行用戶端輪詢,在微信的用戶端每隔一段時間(比如:1s或者2s),向服務端發送一個請求,查詢是否有新的消息,如果有消息就顯示紅點。這種方法是不是太笨了呢?每次都要用戶端去發起請求,難道就不能從服務端發起請求嗎?這樣用戶端不就省事了嗎。再看看股票軟體,每個股票的目前價格都是實時的,這我們怎麼做,每個一秒請求背景查詢目前股票的價格嗎?這樣效率也太低了吧,而且時效性也很低。這就需要我們今天的主角WebSocket去實作了。
什麼是WebSocket
WebSocket協定,
RFC 6455這個大家有興趣可以看看,太深,太底層。它是通過一個TCP連接配接,在客服端與服務端之間建立的一個全雙工、雙向的通信管道。它是一個不同于HTTP的TCP協定,但是它通過HTTP工作。它的預設端口也是80和443,和HTTP是一樣的。
一個WebSocket的互動開始于一個HTTP請求,這是一個握手請求,這個請求中包含一個Upgrade請求頭,具體如下:
GET /spring-websocket-portfolio/portfolio HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Uc9l9TMkWGbHFD2qnFHltg==
Sec-WebSocket-Protocol: v10.stomp, v11.stomp
Sec-WebSocket-Version: 13
Origin: http://localhost:8080
我們看到的第3行和第4行就是這個特殊的請求頭,既然包含了這個特殊的請求頭,那麼請求就要更新,更新成WebSocket請求。這個握手請求的響應也比較特殊,它的成功狀态碼是101,而不是HTTP的200,如下:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 1qVdfYHU9hPOl4JYYNXF623Gzn0=
Sec-WebSocket-Protocol: v10.stomp
在這次成功的握手請求以後,在用戶端和服務端之間的socket被打開,用戶端和服務端可以進行消息的發送和接收。
程式實作
我們還是使用現在流程的SpringBoot去搭建我們的項目,在項目中,我們添加兩個依賴,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- spring-boot-starter-websocket,這是我們今天的主角,我們WebSocket的實作都依賴于這個jar包;
- spring-boot-starter-thymeleaf,這隻是起個輔助作用,在項目中要寫個頁面;
好了,基礎工作準備好了,下面進入最核心的代碼,先寫個WebSocketHandler,這個是用于在服務端接收和傳回消息使用的。如下:
public class MyHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
String payload = message.getPayload();
System.out.println("我接收到的消息:"+payload);
String rtnMsg = "我回複了";
for (int i=0;i<10;i++) {
Thread.sleep(2000);
session.sendMessage(new TextMessage(rtnMsg+i));
}
super.handleTextMessage(session, message);
}
}
- 我們建立一個MyHandler類,繼承TextWebSocketHandler類,這個類主要是處理文本的,當然也可以繼承其他的類,比如:處理二進制的BinaryWebSocketHandler;
- 然後,我們實作handleTextMessage方法,這個方法有兩個參數,WebSocketSession和TextMessage,TextMessage是接收用戶端發來的消息。WebSocketSession用于設定WebSocket會話和向用戶端發送消息;
- 在具體的方法實作中,我們調用TextMessage的getPayload方法,可以取出用戶端發送的消息;
- 最後我們通過WebSocketSession的sendMessage方法向用戶端發送消息,這裡進行10次循環,每次循環我們間隔2秒;
好了,到這裡最核心的處理接收消息的方法,我們已經寫好了,然後我們将這個handler指定一個URL,如下:
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(),"/websocket");
}
@Bean
public MyHandler myHandler() {
return new MyHandler();
}
}
- 首先,我們寫一個WebSocket的配置類WebSocketConfig去實作WebSocketConfigurer接口;
- 由于這是一個配置類,是以在類上加上注解@Configuration,同時因為要做WebSocket的配置,還要加上@EnableWebSocket這個注解;
- 這個類要實作注冊WebSocketHandler的方法registerWebSocketHandlers,在這裡,我們将前面寫的Handler映射到/websocket這個URL;
- 執行個體化前面寫的MyHandler這個類;
到這裡,WebSocket的服務端的内容就寫好了,接下來,我們再寫個簡單的頁面,在頁面中,使用js進行socket的調用,具體頁面内容如下:
<body>
<div id="msg"></div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
var socket = new WebSocket("ws://localhost:8080/websocket");
socket.onopen = function() {
socket.send("發送資料");
console.log("資料發送中...");
};
socket.onmessage = function (evt){
var received_msg = evt.data;
$("#msg").append(received_msg+'<br>');
console.log("資料已接收..."+received_msg);
};
socket.onclose = function(){
// 關閉 websocket
console.log("連接配接已關閉...");
};
</script>
</body>
- 我們先寫個div,在這個div中展示服務端傳回内容;
- 引入jquery,主要進行div内容的操作;
- 在第二個script中,我們進行websocket的連接配接,注意,協定名稱是ws,位址就是我們在WebSocketConfig中配置的位址;
- 接下來就是onopen,onmessage,onclose方法,分别對應着socket打開,接收服務端消息和socket關閉的方法。我們在onmessage方法中,接收到服務端的消息,将其添加到div當中。
最後,我們再給這個html頁面寫個controller映射,如下:
@Controller
public class MyController {
@RequestMapping("index")
public String index() {
return "index";
}
}
這個就不過多解釋了,我們啟動一下應用,在浏覽器中通路一下這個html頁面吧。
- 我們通路的連接配接是: http://localhost:8080/index ,這對應我們寫的html頁面;
- 在這個頁面中,我們通過js通路了服務端的websocket;
- socket連接配接成功後,每隔2s向服務端發送一條消息;
- 在html頁面中,通過onmessage方法接收消息,并将消息添加到div當中;
如果使用以前輪詢的方法,我們需要在html頁面中,定時輪詢請求背景。而現在,我們通過websocket,服務端可以向用戶端發送消息,大大提高了效率。
好了,通過Spring整合WebSocket就先給大家介紹到這裡了。