WebSocket
1.介紹
WebSocket是基于TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工通信——瀏覽器和服務器只需要一次握手,兩者之間就可以創建持久性的連接,并進行雙向數據傳送。
HTTP協議和WebSocket協議對比:
①Http是短連接
②WebSocket是長連接
③Http通信是單向的,基于請求響應模式
④WebSocket支持雙向通信
⑤Http和WebSocket底層都是TCP連接
應用場景:
①視頻彈幕
②網頁聊天
③體育實況更新
④股票基金報價實時更新
效果展示:
2.入門案例
實現步驟:
①直接使用websocket.html頁面作為WebSocket客戶端
<!DOCTYPE HTML>
<html>
<head><meta charset="UTF-8"><title>WebSocket Demo</title>
</head>
<body><input id="text" type="text" /><button onclick="send()">發送消息</button><button onclick="closeWebSocket()">關閉連接</button><div id="message"></div>
</body>
<script type="text/javascript">var websocket = null;var clientId = Math.random().toString(36).substr(2);//判斷當前瀏覽器是否支持WebSocketif('WebSocket' in window){//連接WebSocket節點websocket = new WebSocket("ws://localhost:8080/ws/"+clientId);}else{alert('Not support websocket')}//連接發生錯誤的回調方法websocket.onerror = function(){setMessageInnerHTML("error");};//連接成功建立的回調方法websocket.onopen = function(){setMessageInnerHTML("連接成功");}//接收到消息的回調方法websocket.onmessage = function(event){setMessageInnerHTML(event.data);}//連接關閉的回調方法websocket.onclose = function(){setMessageInnerHTML("close");}//監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。window.onbeforeunload = function(){websocket.close();}//將消息顯示在網頁上function setMessageInnerHTML(innerHTML){document.getElementById('message').innerHTML += innerHTML + '<br/>';}//發送消息function send(){var message = document.getElementById('text').value;websocket.send(message);}//關閉連接function closeWebSocket() {websocket.close();}
</script>
</html>
②導入WebSocket的Maven坐標
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
③導入WebSocket服務端組件WebSocketServer,用于和客戶端通信
package com.sky.websocket;import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** WebSocket服務*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {//存放會話對象private static Map<String, Session> sessionMap = new HashMap();/*** 連接建立成功調用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客戶端:" + sid + "建立連接");sessionMap.put(sid, session);}/*** 收到客戶端消息后調用的方法** @param message 客戶端發送過來的消息*/@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到來自客戶端:" + sid + "的信息:" + message);}/*** 連接關閉調用的方法** @param sid*/@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("連接斷開:" + sid);sessionMap.remove(sid);}/*** 群發** @param message*/public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服務器向客戶端發送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}
④導入配置類WebSocketConfiguration,注冊WebSocket的服務端組件
package com.sky.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** WebSocket配置類,用于注冊WebSocket的Bean*/
@Configuration
public class WebSocketConfiguration {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}
⑤導入定時任務類WebSocketTask,定時向客戶端推送數據
package com.sky.task;import com.sky.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;@Component
public class WebSocketTask {@Autowiredprivate WebSocketServer webSocketServer;/*** 通過WebSocket每隔5秒向客戶端發送消息*/@Scheduled(cron = "0/5 * * * * ?")//這個在后面的代碼里還是注釋掉好一些,不然語音播報會一直播報public void sendMessageToClient() {webSocketServer.sendToAllClient("這是來自服務端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}
}
來單提醒
1.實現思路
用戶下單并支付成功后,需要第一時間通知外賣商家。通知的形式有如下兩種:
①語音播報
②彈出提示框
設計:
①通過WebSocket實現管理端頁面和服務端保持長連接狀態
②當客戶支付后,調用WebSocket的相關API實現服務端向客戶端推送消息
③客戶端瀏覽器解析服務端推送消息,判斷是來單提醒還催單,進行相應的消息提示和語音播報
④約定服務端發送給客戶端瀏覽器的數據格式為JSON,字段包括:type,orderId,content
? ? ? ? -type為消息類型,1為來單提醒 2為客戶催單
? ? ? ? -orderId為訂單id
? ? ? ? -content為消息內容
2.實現代碼
在OrderServiceImpl.java中的paySucess方法中添加如下代碼:
//通過websocket向客戶端瀏覽器推送消息type orderId contextMap map = new HashMap();map.put("type",1);//1表示來單提醒 2表示客戶催單map.put("orderId",ordersDB.getId());map.put("content","訂單號:"+outTradeNo);String json = JSON.toJSONString(map);webSocketServer.sendToAllClient(json);
客戶催單
1.實現思路
用戶在小程序中點擊催單按鈕后,需要第一時間通知外賣商家。通知的形式有如下兩種:
①語音播報
②彈出提示框
設計:
①通過WebSocket實現管理端頁面和服務端保持長連接狀態
②當客戶支付后,調用WebSocket的相關API實現服務端向客戶端推送消息
③客戶端瀏覽器解析服務端推送消息,判斷是來單提醒還催單,進行相應的消息提示和語音播報
④約定服務端發送給客戶端瀏覽器的數據格式為JSON,字段包括:type,orderId,content
? ? ? ? -type為消息類型,1為來單提醒 2為客戶催單
? ? ? ? -orderId為訂單id
? ? ? ? -content為消息內容
接口設計:
2.實現代碼
OrderController.java代碼:
/*** 客戶催單* @param id* @return*/@GetMapping("/reminder/{id}")@ApiOperation("客戶催單")public Result reminder(@PathVariable("id") Long id){orderService.reminder(id);return Result.success();}
OrderService.java代碼:
/*** 客戶催單* @param id*/void reminder(Long id);
OrderServiceImpl.java代碼:
/*** 客戶催單* @param id*/public void reminder(Long id){// 根據id查詢訂單Orders ordersDB = orderMapper.getById(id);// 校驗訂單是否存在if (ordersDB == null) {throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}//通過websocket向客戶端瀏覽器推送消息type orderId contextMap map = new HashMap();map.put("type",2);//1表示來單提醒 2表示客戶催單map.put("orderId",id);map.put("content","訂單號:"+ordersDB.getNumber());String json = JSON.toJSONString(map);webSocketServer.sendToAllClient(json);}