WebSocket實現服務端推送訊息和聊天會話
今天在做後臺管理系統,需要實現客服會話聊天 和 服務端推送訊息等功能。我們平常的HTTP無法滿足長連線,如果一直請求伺服器會造成資源的浪費,所以http協議就行不通了,這時候就需要使用WebSocket技術,是獨立與http協議的。
廢話不多說,我整理了使用的操作步驟,如下。
首先,需要引入maven依賴,因為我的工程是springboot+mabatis,所以匯入的依賴如下:
<!-- 熱啟動依賴包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- jdbc、mybatis、springboot配合
事物、oracle、pagehelper、fastjson
log4j、aop、日誌 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0-atlassian-hosted</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
新建service包,其下建一個類WebSocketServer用於連線通訊( 注意:不要使用Aop掃描這個類,會報錯!)
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
import org.slf4j.LoggerFactory;
@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketServer {
static Logger log =LoggerFactory.getLogger(WebSocketServer.class);
/**靜態變數,用來記錄當前線上連線數。應該把它設計成執行緒安全的。*/
private static int onlineCount = 0;
/**concurrent包的執行緒安全Set,用來存放每個客戶端對應的MyWebSocket物件。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**與某個客戶端的連線會話,需要通過它來給客戶端傳送資料*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 連線建立成功呼叫的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
//加入set中
}else{
webSocketMap.put(userId,this);
//加入set中
addOnlineCount();
//線上數加1
}
log.info("使用者連線:"+userId+",當前線上人數為:" + getOnlineCount());
try {
sendMessage("連線成功");
} catch (IOException e) {
log.error("使用者:"+userId+",網路異常!!!!!!");
}
}
/**
* 連線關閉呼叫的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
//從set中刪除
subOnlineCount();
}
log.info("使用者退出:"+userId+",當前線上人數為:" + getOnlineCount());
}
/**
* 收到客戶端訊息後呼叫的方法
*
* @param message 客戶端傳送過來的訊息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("使用者訊息:"+userId+",報文:"+message);
//可以群發訊息
//訊息儲存到資料庫、redis
if(StringUtils.isNotBlank(message)){
try {
//解析傳送的報文
JSONObject jsonObject = JSON.parseObject(message);
//追加傳送人(防止串改)
jsonObject.put("fromUserId",this.userId);
String toUserId=jsonObject.getString("toUserId");
//傳送給對應toUserId使用者的websocket
if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){
webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
}else{
log.error("請求的userId:"+toUserId+"不在該伺服器上");
//否則不在這個伺服器上,傳送到mysql或者redis
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("使用者錯誤:"+this.userId+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 實現伺服器主動推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 傳送自定義訊息
* */
public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("傳送訊息到:"+userId+",報文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
log.error("使用者"+userId+",不線上!");
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
@ServerEndpoint("/imserver/{userId}")
@Component 例項化spring容器中,實現@OnOpen開啟連線,@onClose關閉連線,@onMessage接收訊息等方法
新建訊息傳送的Control包,建DemoController類
@RestController
public class DemoController {
@GetMapping("index")
public ResponseEntity<String> index(){
return ResponseEntity.ok("請求成功");
}
@GetMapping("page")
public ModelAndView page(){
return new ModelAndView("websocket");
}
@RequestMapping("/push/{toUserId}")
public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserId) throws IOException {
WebSocketServer.sendInfo(message,toUserId);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
}
頁面發起請求
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket通訊</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var socket;
function openSocket() {
if(typeof(WebSocket) == "undefined") {
console.log("您的瀏覽器不支援WebSocket");
}else{
console.log("您的瀏覽器支援WebSocket");
//實現化WebSocket物件,指定要連線的伺服器地址與埠 建立連線
//等同於socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
//var socketUrl="${request.contextPath}/im/"+$("#userId").val();
var socketUrl="http://localhost:10086/imserver/"+$("#userId").val();
socketUrl=socketUrl.replace("https","ws").replace("http","ws");
console.log(socketUrl);
if(socket!=null){
socket.close();
socket=null;
}
socket = new WebSocket(socketUrl);
//開啟事件
socket.onopen = function() {
console.log("websocket已開啟");
//socket.send("這是來自客戶端的訊息" + location.href + new Date());
};
//獲得訊息事件
socket.onmessage = function(msg) {
console.log(msg.data);
//發現訊息進入開始處理前端觸發邏輯
};
//關閉事件
socket.onclose = function() {
console.log("websocket已關閉");
};
//發生了錯誤事件
socket.onerror = function() {
console.log("websocket發生了錯誤");
}
}
}
function sendMessage() {
if(typeof(WebSocket) == "undefined") {
console.log("您的瀏覽器不支援WebSocket");
}else {
console.log("您的瀏覽器支援WebSocket");
console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
}
}
</script>
<body>
<p>【userId】:<div><input id="userId" name="userId" type="text" value="10"></div>
<p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
<p>【messsage】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【操作】:<div><button onclick="openSocket()">開啟socket</button></div>
<p>【操作】:<div><button onclick="sendMessage()">傳送訊息</button></div>
</body>
</html>
最後啟動專案
網址是:http://localhost:埠號/page.do?p=index
也可以開啟另一個新頁面手動自定義傳送訊息,這樣在10埠的控制檯也可以列印我們位址列傳送的訊息
相關文章
- Spring Boot 整合 WebSocket 實現服務端推送訊息到客戶端Spring BootWeb服務端客戶端
- 利用WebSocket和EventSource實現服務端推送Web服務端
- springboot2整合websocket,實現服務端推送訊息到客戶端Spring BootWeb服務端客戶端
- Flutter websocket 實現訊息推送FlutterWeb
- WebSocket 實現伺服器訊息推送客戶端Web伺服器客戶端
- 基於 Hyperf 實現 RabbitMQ + WebSocket 訊息推送MQWeb
- 服務端主動推送技術☞WebSocket服務端Web
- 訊息的即時推送——net實現、websocket實現以及socket.io實現Web
- workerman 實現訊息推送
- 微信雲託管 WebSocket 實戰:基於模版實現訊息推送Web
- Fastapi整合SSE服務後端主動推送訊息到前端ASTAPI後端前端
- java netty 實現 websocket 服務端和客戶端雙向通訊 實現心跳和斷線重連 完整示例JavaNettyWeb服務端客戶端
- 【PWA學習與實踐】(5)在Web中進行服務端訊息推送Web服務端
- 基於APNs最新HTTP/2介面實現iOS的高效能訊息推送(服務端篇)HTTPiOS服務端
- WebRTC + WebSocket 實現視訊通話Web
- mPaaS 服務端核心元件:訊息推送 MPS 架構及流程設計服務端元件架構
- 用訊息佇列和socket實現聊天系統佇列
- Qt實現網路聊天室(客戶端,服務端)QT客戶端服務端
- 如何實現在服務端錄製視訊會議?服務端
- Vue +WebSocket + WaveSurferJS 實現H5聊天對話互動VueWebJSH5
- 使用開源ntfy訊息推送服務釋出通知實現全平臺接收通知
- PHP與反ajax推送,實現的訊息實時推送功能PHP
- WebSocket的故事(五)—— Springboot中,實現網頁聊天室之自定義訊息代理WebSpring Boot網頁
- WebSocket實現前後端通訊Web後端
- Knative 實戰:基於 Kafka 實現訊息推送Kafka
- Android端實現多人音視訊聊天應用(二):多人視訊通話Android
- 實時訊息推送整理
- 實現服務端和客戶端的實時雙向資料傳輸-WebSocket簡單瞭解服務端客戶端Web
- websocket(推送服務)初始化及實際專案運用Web
- WebWorker與WebSocket實現前端訊息匯流排Web前端
- 用 Golang 實現百萬級 Websocket 服務GolangWeb
- 如何實現從 Redis 中訂閱訊息轉發到 WebSocket 客戶端RedisWeb客戶端
- PHP基於Redis訊息佇列實現的訊息推送的方法PHPRedis佇列
- java WebSocket 服務端程式碼JavaWeb服務端
- C#建立WebSocket服務端C#Web服務端
- Laravel 實現 Kafka 訊息推送與接收處理LaravelKafka
- 使用swoole作為MQTT客戶端並接收實現即時訊息推送MQQT客戶端
- kafka和websocket實時資料推送KafkaWeb