聊天消息實時推送(springBoot,webSocket)

使用springBoot集成的webSocket實現實時消息推送(模仿聊天)

java代碼

package com.lsbj.biz.scoket;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

/**
 * @Auther: zs
 * @Date: 2019/1/17 11:04
 * @Description: *
 * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket服務器端,
 * 註解的值將被用於監聽用戶連接的終端訪問URL地址,客戶端可以通過這個URL來連接到WebSocket服務器端
 */
@ServerEndpoint("/ws/push/{userId}")
@Component
public class WebSocketDemo {

    // Hashtablex線程安全的map用來存儲一登錄用戶信息,key爲用戶id。
    private static Hashtable<String, WebSocketDemo> userMap = new Hashtable<>();

    // 靜態變量,用來記錄當前在線連接數。
    private static int onlineCount = 0;

    // 與客戶端的連接會話。
    private Session session;

    // 與客戶端的連接的用戶id。
    private String userId;

    //連接打開時執行
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) throws IOException{
        System.out.println("新客戶端接入,用戶ID:" + userId);
        System.out.println("在線人數:" + WebSocketDemo.onlineCount);
        if(StringUtils.isNotBlank(userId)){
            this.userId = userId;
            this.session = session;
            userMap.put(userId,this); // 加入set中
            addOnlineCount(); // 在線數加1
        }
        System.out.println("在線人數:" + WebSocketDemo.onlineCount);
    }

    //連接關閉調用的方法
    @OnClose
    public void onClose() {
        System.out.println("客戶端關閉連接:"+this.userId);
        userMap.remove(this.userId); // 從map中刪除
        subOnlineCount(); // 在線數減1
        System.out.println("在線人數:" + WebSocketDemo.onlineCount);
    }

    //收到客戶端消息後調用的方法
    @OnMessage
    public void onMessage(String message, Session session) {
        if(StringUtils.isNotBlank(this.userId)){
            if(!"ping".equals(message)){//不是心跳檢測
                //收到消息後可以去做一些具體的業務處理在推送,此處直接推送
                sendAll("【"+this.userId+"】"+message);
            }
        }else{
            System.out.println("當前客戶未登陸:"+this.userId);
        }
        System.out.println("用戶【"+this.userId+"】訪問");
    }

    //發生錯誤時調用
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessage(String userId,String message){
        try {
            if(!StringUtils.isNotBlank(userId)){
                System.out.println("客戶ID不能爲空");
                return ;
            }
            for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
                if(entry.getKey().equals(userId)){
                    entry.getValue().getSession().getBasicRemote().sendText(message);
                    System.out.println("推送給用戶【"+entry.getKey()+"】消息成功,消息爲:" + message);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(List<String> userIds,String message){
        try {
            if(userIds == null || userIds.size() == 0){
                System.out.println("客戶ID不能爲空");
                return ;
            }
            for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
                if(userIds.contains(entry.getKey())){
                    entry.getValue().getSession().getBasicRemote().sendText(message);
                    System.out.println("推送給用戶【"+entry.getKey()+"】消息成功,消息爲:" + message);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendAll(String message){
        try {
            if(!StringUtils.isNotBlank(userId)){
                System.out.println("客戶ID不能爲空");
                return ;
            }
            for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
                entry.getValue().getSession().getBasicRemote().sendText(message);
                System.out.println("推送給用戶【"+entry.getKey()+"】消息成功,消息爲:" + message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //獲取連接人數
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
    //連接人數加一
    public static synchronized void addOnlineCount() {
        onlineCount+=1;
    }
    //連接人數減一
    public static synchronized void subOnlineCount() {
        if(onlineCount > 0){
            onlineCount-=1;
        }
    }

    public Session getSession() {
        return session;
    }

}

頁面(HTML)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <title>保存更新</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
    <style>
        input,textarea{
            box-sizing: border-box;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
        }
    </style>
</head>
<body>
<input name="user" id="user" type="text" style="width: 100px"><button type="button" id="login">登錄</button>
<form style="width: 300px;">
    <input name="tenantIds" placeholder="" style="width:100%;"  ><br>
    <button type="button" id="send" style="width:100%;" >提交</button><br>
    <div id="result" placeholder="結果提示" style="width:100%;height: 500px;border: gray solid 1px" ></div>
</form>
</body>
<script>
   var webSocket = null;
   $(document).ready(function () {
       //模擬不同用戶,實際開發中從session或redis裏取
       //判斷當前瀏覽器是否支持WebSocket
       $("#login").click(function () {
           var user = $("#user").val();
           if("WebSocket" in window) {
               webSocket = new WebSocket("ws://localhost:8080/ws/push/" + user);
               displayMsg('用戶【' + user + '】嘗試連接')

               //連接發生錯誤的回調方法
               webSocket.onerror = function(){
                   displayMsg("服務器連接【異常】");
               };


               //連接成功建立的回調方法
               webSocket.onopen = function(event){
                   displayMsg('用戶【'+user+'】服務器連接【成功】')
                   setInterval(function () {
                       webSocket.send("ping")
                   },10000)
               }


               //接收到消息的回調方法
               webSocket.onmessage = function(event){
                   displayMsg(event.data);
               }


               //連接關閉的回調方法
               webSocket.onclose = function(){
                   displayMsg("連接關閉");
               }


               //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
               window.onbeforeunload = function(){
                   websocket.close();
               }
           }else{
               displayMsg('當前瀏覽器可能不支持WebSocket')
           }
       })

       //關閉連接
       function closeWebSocket(){
           websocket.close();
       }

       $("#send").click(function () {
           var message = $("input[name=tenantIds]").val();
           if (message == "") {
               displayMsg("訪問消息不能爲空")
           } else {
               webSocket.send(message);
               displayMsg("已發送:" + message)
           }
       })

       //將消息顯示在網頁上
       function displayMsg(innerHTML){
           document.getElementById('result').innerHTML += innerHTML + '<br/>';
       }
   })
</script>
</html>

實現實時推送(模仿聊天)

分別用:甲、乙、丙三個用戶模擬登陸,丙統計登錄人員
甲登錄:

乙登錄:
丙登錄:

頁面效果甲發消息

其他用戶發消息和甲一樣,都會推送,如乙回覆消息:

關閉一個用戶(關閉瀏覽器或退出)

會觸發退出事件,關閉連接,修改在線人數

導包

在網上看很多都說需要引入整合包,(但是我這沒引用也是正常的,也可能是其他地方引用了)

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

注意:WebSocket類中的onOpen,onClose等 的方法參數不能亂寫,否則可能會報異常(部署異常,沒有註解之類的)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章