實現(xiàn)web端的推送目前有幾種方式:
* 輪詢
客戶端定時向服務(wù)器發(fā)送ajax請求,服務(wù)器接到請求后馬上返回響應(yīng)信息并關(guān)閉連接。
優(yōu)點:后端程序編寫簡單
缺點:請求中有大多是無用的,浪費帶寬和服務(wù)器資源。
示例:適用于小型應(yīng)用
* websocket
WebSocket是HTML5開始提供的一種瀏覽器與服務(wù)器間進行全雙工通訊的網(wǎng)絡(luò)技術(shù)。依靠這種技術(shù)可以實現(xiàn)客戶端和服務(wù)器端的長連接,雙向?qū)崟r通信。在 WebSocket API 中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。
優(yōu)點:節(jié)省服務(wù)器資源和帶寬,實時進行通信
缺點:少部分瀏覽器不支持,且不同瀏覽器支持的程度和方式有區(qū)別
image.png
那么接下來說明一下自己寫的一個超簡單的demo,關(guān)于接收消息的一些邏輯控制未寫入代碼,此處只做簡單的推送功能展示。
1、服務(wù)端
* 引入jar包:
敲重點:注意scope作用域, provided--在編譯和測試的過程有效,最后生成war包時不會加入
<!-- 引入websocket -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
* 消息處理類(注意前后端websocket對象是同一個)
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/websocket/endpoint")
public class WebsocketHandler {
private static Map<String, WebsocketHandler> clients = new ConcurrentHashMap<String, WebsocketHandler>();
@OnOpen
public void onOpen(Session session) throws IOException {
//如果有用戶信息,可以將用戶編碼之類的唯一標(biāo)識作為clients的key,這樣可以保證一個用戶只有一個websocket client有效(此處的“1”僅僅作為demo的測試?yán)樱?
//如果同一個用戶可以擁有多個websocket client,可以將session.getId()作為key,根據(jù)實際業(yè)務(wù)需求來設(shè)置即可
clients.put("1", this);
System.out.println("已連接");
}
@OnMessage
public void onMessage(String message) {
//以下代碼省略...
System.out.println(message);
for (WebsocketHandler client : clients.values()) {
client.session.getBasicRemote().sendText(message);
}
}
@OnError
public void onError(Throwable t) {
//以下代碼省略...
t.printStackTrace();
}
@OnClose
public void onClose(Session session, CloseReason reason) {
//以下代碼省略...
System.out.println(String.format("Session %s closed because of %s", session.getId(), reason));
System.out.println("已關(guān)閉連接");
}
public static void pushMsg(String message){
// “1”只是測試用的key
WebsocketHandler client = clients.get("1");
try {
if(client != null){
client.session.getBasicRemote().sendText(message);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
* 后端對外消息推送接口
@ResponseBody
@RequestMapping(value="/sendMessage", method = RequestMethod.GET)
public void sendMessage() throws IOException {
WebsocketHandler.pushMsg("這是我需要發(fā)送的消息,記得給我傳達,后面可以做成通用的接口");
}
2、客戶端
<!-- html -->
<button onclick="sendMessage()">發(fā)送消息</button>
<button onclick="closeWebSocket()">關(guān)閉</button>
<div>這是顯示結(jié)果的地方:
<p id="message" style="color:red;"></p>
</div>
<!-- javascript -->
<script type="text/javascript" src="../js/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
var websocket = null;
var url = "ws://localhost:8081/websocket-demo/websocket/endpoint";
$(document).ready(function(){
//判斷當(dāng)前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
alert("瀏覽器支持Websocket")
websocket = new WebSocket(url);
} else {
alert('當(dāng)前瀏覽器 Not support websocket');
}
if(websocket != null){
//連接發(fā)生錯誤的回調(diào)方法
websocket.onerror = function() {
alert("WebSocket連接發(fā)生錯誤")
setMessageInnerHTML("WebSocket連接發(fā)生錯誤");
};
//連接成功建立的回調(diào)方法
websocket.onopen = function() {
alert("WebSocket連接成功")
setMessageInnerHTML("WebSocket連接成功");
}
//接收到消息的回調(diào)方法
websocket.onmessage = function(event) {
alert("接收到消息的回調(diào)方法")
alert("這是后臺推送的消息:"+event.data);
// websocket.close();
// alert("webSocket已關(guān)閉!")
}
//連接關(guān)閉的回調(diào)方法
websocket.onclose = function() {
setMessageInnerHTML("WebSocket連接關(guān)閉");
}
}
});
//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
window.onbeforeunload = function() {
closeWebSocket();
}
//關(guān)閉WebSocket連接
function closeWebSocket() {
websocket.close();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML) {
$("#message").text(innerHTML);
}
</script>