国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

SpringBoot2.0集成WebSocket,多客戶端

這篇具有很好參考價值的文章主要介紹了SpringBoot2.0集成WebSocket,多客戶端。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

適用于單客戶端,一個賬號登陸一個客戶端,登陸多個客戶端會報錯

The remote endpoint was in state [TEXT_FULL_WRITING]?

這是因為此時的session是不同的,只能鎖住一個session,解決此問題的方法把全局靜態(tài)對象鎖住,因為賬號是唯一的文章來源地址http://www.zghlxwxcb.cn/news/detail-689135.html

/**
 * @Description 開啟springboot對websocket的支持
 * @Author WangKun
 * @Date 2023/8/14 17:21
 * @Version
 */
@ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
@Configuration
public class WebSocketConfig{

    /**
     * @Description 注入一個ServerEndpointExporter, 會自動注冊使用@ServerEndpoint注解
      * @param
     * @Throws
     * @Return org.springframework.web.socket.server.standard.ServerEndpointExporter
     * @Date 2023-08-14 17:26:31
     * @Author WangKun
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
/**
 * @Description websocket服務(wù),不考慮分組
 * @Author WangKun
 * @Date 2023/8/14 17:29
 * @Version
 */
@ConditionalOnClass(value = WebSocketConfig.class)
@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocket {

    private static final long SESSION_TIMEOUT = 60000;

    //存放每個客戶端對應(yīng)的WebSocket對象。
    private static final ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocket>> WEB_SOCKET_MAP = new ConcurrentHashMap<>();

    //與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
    private Session session;
    private String userId;

    /**
     * @param o
     * @Description 重寫防止session重復(fù)
     * @Throws
     * @Return boolean
     * @Date 2023-09-01 10:02:51
     * @Author WangKun
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        WebSocket that = (WebSocket) o;
        return Objects.equals(session, that.session);
    }

    @Override
    public int hashCode() {
        return Objects.hash(session);
    }

    /**
     * @param session
     * @param userId
     * @Description 建立連接
     * @Throws
     * @Return void
     * @Date 2023-08-14 17:52:08
     * @Author WangKun
     */
    @SneakyThrows
    @OnOpen
    public void onOpen(final Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        session.setMaxIdleTimeout(SESSION_TIMEOUT);
        //先查找是否有uniCode
        CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(userId);
        if (users == null) {
            //處理多個同時連接并發(fā)
            synchronized (WEB_SOCKET_MAP) {
                if (!WEB_SOCKET_MAP.contains(userId)) {
                    users = new CopyOnWriteArraySet<>();
                    WEB_SOCKET_MAP.put(userId, users);
                }
            }
        }
        users.add(this);
        sendMessage(String.valueOf(ResponseCode.CONNECT_SUCCESS.getCode()));
        log.info("用戶--->{} 連接成功,當(dāng)前在線人數(shù)為--->{}", userId, WEB_SOCKET_MAP.size());
    }

    /**
     * @param message
     * @Description 向客戶端發(fā)送消息 session.getBasicRemote()與session.getAsyncRemote()的區(qū)別
     * @Throws
     * @Return void
     * @Date 2023-08-14 17:51:07
     * @Author WangKun
     */
    @SneakyThrows
    public void sendMessage(String message) {
        // 加鎖避免阻塞
        // 如果有多個客戶端的話,亦或者同一個用戶,或者打開了多個瀏覽器(同一個用戶打開多個客戶端或者多個界面),開了多個頁面,此時Session是不同的,只能鎖住一個session,所以鎖住全局靜態(tài)對象
//        synchronized(session) {
//            this.session.getBasicRemote().sendText(message);
//        }
        synchronized (WEB_SOCKET_MAP) {
            CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(userId);
            if (users != null) {
                for (WebSocket user : users) {
                    // 判斷當(dāng)前客戶端的用戶是否打開連接
                    if (user.session.isOpen()) {
                        user.session.getBasicRemote().sendText(message);
                        log.info("向客戶端發(fā)送數(shù)據(jù)--->{} 數(shù)據(jù)為--->{}", userId, message);
                    }
                }
            }
        }
    }

    /**
     * @param
     * @Description 關(guān)閉連接
     * @Throws
     * @Return void
     * @Date 2023-08-14 17:52:30
     * @Author WangKun
     */
    @SneakyThrows
    @OnClose
    public void onClose(Session session) {
        // 避免多人同時在線直接關(guān)閉通道。
        CopyOnWriteArraySet<WebSocket> copyOnWriteArraySet = WEB_SOCKET_MAP.get(this.userId);
        if (!copyOnWriteArraySet.isEmpty()) {
            Object[] objects = copyOnWriteArraySet.toArray();
            for (Object object : objects) {
                if (((WebSocket) object).session.equals(session)) {
                    //刪除當(dāng)前用戶
                    WEB_SOCKET_MAP.get(this.userId).remove((WebSocket) object);
                    // 如果有一個客戶端登陸 下線清除用戶
                    if (WEB_SOCKET_MAP.get(this.userId).isEmpty()) {
                        WEB_SOCKET_MAP.remove(this.userId);
                    }
                    CloseReason close = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "關(guān)閉客戶端,下線!");
                    session.close(close);
                    log.info("用戶--->{} 關(guān)閉連接!", userId);
                }
            }
        }
    }

    /**
     * @param message
     * @param session
     * @Description 收到客戶端消息
     * @Throws
     * @Return void
     * @Date 2023-08-15 10:54:55
     * @Author WangKun
     */
    @SneakyThrows
    @OnMessage
    public void onMessage(String message, Session session) {
        //枷鎖避免多個資源互搶
        //這一塊可以操作數(shù)據(jù),比如存到數(shù)據(jù)

        // 同一個用戶,多個地方登錄(多個session),循環(huán)發(fā)送消息,
        // 如果有多個客戶端的話,亦或者同一個用戶,或者打開了多個瀏覽器,開了多個頁面,此時Session是不同的,只能鎖住一個session,所以鎖住全局靜態(tài)對象
        synchronized (WEB_SOCKET_MAP) {
            CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(userId);
            if (users != null) {
                for (WebSocket user : users) {
                    if (user.session.isOpen()) {
                        user.session.getBasicRemote().sendText("pong");
                        log.info("收到客戶端發(fā)送的心跳的數(shù)據(jù)--->{} 數(shù)據(jù)為--->{}", userId, message);
                    }
                }
            }
        }
    }

    /**
     * @param session
     * @param error
     * @Description 發(fā)生錯誤時
     * @Throws
     * @Return void
     * @Date 2023-08-15 10:55:27
     * @Author WangKun
     */
    @SneakyThrows
    @OnError
    public void onError(Session session, Throwable error) {
        CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(userId);
        if (!users.isEmpty()) {
            Object[] objects = users.toArray();
            for (Object object : objects) {
                if (((WebSocket) object).session.equals(session)) {
                    //刪除當(dāng)前用戶
                    WEB_SOCKET_MAP.get(this.userId).remove((WebSocket) object);
                    // 如果有一個客戶端登陸 下線清除用戶
                    if (WEB_SOCKET_MAP.get(this.userId).isEmpty()) {
                        WEB_SOCKET_MAP.remove(this.userId);
                    }
                    CloseReason close = new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "異常,下線!");
                    session.close(close);
                    log.error("用戶--->{} 錯誤!" + userId, "原因--->{}" + error.getMessage(), error);
                }
            }
        }
//        WEB_SOCKET_MAP.remove(userId);
//        log.error("用戶--->{} 錯誤!" + userId, "原因--->{}" + error.getMessage(), error);
    }


    /**
     * @param userId
     * @param message
     * @Description 通過userId向客戶端發(fā)送消息(指定用戶發(fā)送)
     * @Throws
     * @Return void
     * @Date 2023-08-14 18:01:35
     * @Author WangKun
     */
    public static void sendTextMessageByUserId(String userId, String message) {
        CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(userId);
        if (users != null) {
            for (WebSocket user : users) {
                user.sendMessage(message);
                log.info("服務(wù)端發(fā)送消息到用戶{},消息:{}", userId, message);
            }
        }
    }

    /**
     * @param message
     * @Description 群發(fā)自定義消息
     * @Throws
     * @Return void
     * @Date 2023-08-14 18:03:38
     * @Author WangKun
     */
    public static void sendTextMessage(String message) {
        // 如果在線一個就廣播
        if (!WEB_SOCKET_MAP.isEmpty()) {
            for (String item : WEB_SOCKET_MAP.keySet()) {
                CopyOnWriteArraySet<WebSocket> users = WEB_SOCKET_MAP.get(item);
                if (users != null) {
                    for (WebSocket user : users) {
                        user.sendMessage(message);
                        log.info("服務(wù)端發(fā)送消息到用戶{},消息:{}", item, message);
                    }
                }
            }
        }
    }
}

到了這里,關(guān)于SpringBoot2.0集成WebSocket,多客戶端的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 快速搭建springboot websocket客戶端

    快速搭建springboot websocket客戶端

    WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進(jìn)行全雙工通訊的協(xié)議。 HTML5 定義的 WebSocket 協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實時地進(jìn)行通訊。 HTML5 定義的 WebSocket 協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實時地進(jìn)行通訊。 瀏覽器通過 JavaSc

    2024年02月06日
    瀏覽(24)
  • Spring Boot 集成 WebSocket 實現(xiàn)服務(wù)端推送消息到客戶端

    Spring Boot 集成 WebSocket 實現(xiàn)服務(wù)端推送消息到客戶端

    ? ? ? 假設(shè)有這樣一個場景:服務(wù)端的資源經(jīng)常在更新,客戶端需要盡量及時地了解到這些更新發(fā)生后展示給用戶,如果是 HTTP 1.1,通常會開啟 ajax 請求詢問服務(wù)端是否有更新,通過定時器反復(fù)輪詢服務(wù)端響應(yīng)的資源是否有更新。 ? ? ? ? ? ? ? ?? ? ? ? ?在長時間不更新

    2024年02月16日
    瀏覽(86)
  • SpringBoot+WebSocket實現(xiàn)服務(wù)端、客戶端

    SpringBoot+WebSocket實現(xiàn)服務(wù)端、客戶端

    小編最近一直在使用springboot框架開發(fā)項目,畢竟現(xiàn)在很多公司都在采用此框架,之后小編也會陸續(xù)寫關(guān)于springboot開發(fā)常用功能的文章。 什么場景下會要使用到websocket的呢? websocket主要功能就是實現(xiàn)網(wǎng)絡(luò)通訊,比如說最經(jīng)典的客服聊天窗口、您有新的消息通知,或者是項目與

    2024年02月13日
    瀏覽(25)
  • kafka:java集成 kafka(springboot集成、客戶端集成)

    kafka:java集成 kafka(springboot集成、客戶端集成)

    摘要 對于java的kafka集成,一般選用springboot集成kafka,但可能由于對接方kafka老舊、kafka不安全等問題導(dǎo)致kafak版本與spring版本不兼容,這個時候就得自己根據(jù)kafka客戶端api集成了。 一、springboot集成kafka 具體官方文檔地址:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/

    2023年04月22日
    瀏覽(92)
  • 二、springboot集成CAS客戶端實現(xiàn)單點登錄

    pom中引入依賴 yml中添加cas配置 讀取CAS相關(guān)配置 cas配置類 單點登錄接口demo 訪問loingCas接口時,若未在CASserver登錄,則會被攔截跳轉(zhuǎn)到CAS的登陸頁面,登陸成功后放行繼續(xù)訪問loginCas接口

    2024年02月15日
    瀏覽(23)
  • SpringBoot集成Elasticsearch客戶端(新舊版本)(2023-01-28)

    SpringBoot集成Elasticsearch客戶端(新舊版本)(2023-01-28)

    第一章 SpringBoot集成ElasticSearch(2023-01-28) 例如:業(yè)務(wù)中需要使用es,所以做一些客戶端選型,熟悉一下基本的操作,所以記錄這篇博客,有關(guān)概念理論性的文章還在整理過程中,后續(xù)會整理個系列 Spring認(rèn)證中國教育管理中心-Spring Data Elasticsearch教程一 SpringData集成Elasticsearch Sp

    2024年02月07日
    瀏覽(24)
  • 【Java】SpringBoot快速整合WebSocket實現(xiàn)客戶端服務(wù)端相互推送信息

    【Java】SpringBoot快速整合WebSocket實現(xiàn)客戶端服務(wù)端相互推送信息

    目錄 什么是webSocket? webSocket可以用來做什么? WebSocket操作類 一:測試客戶端向服務(wù)端推送消息 1.啟動SpringBoot項目 2.打開網(wǎng)站 3.進(jìn)行測試消息推送 4.后端進(jìn)行查看測試結(jié)果 二:測試服務(wù)端向客戶端推送消息 1.接口代碼 2.使用postman進(jìn)行調(diào)用 3.查看測試結(jié)果 ????????WebSocke

    2024年01月20日
    瀏覽(37)
  • Java:SpringBoot整合WebSocket實現(xiàn)服務(wù)端向客戶端推送消息

    Java:SpringBoot整合WebSocket實現(xiàn)服務(wù)端向客戶端推送消息

    思路: 后端通過websocket向前端推送消息,前端統(tǒng)一使用http協(xié)議接口向后端發(fā)送數(shù)據(jù) 本文僅放一部分重要的代碼,完整代碼可參看github倉庫 websocket 前端測試 :http://www.easyswoole.com/wstool.html 依賴 項目目錄 完整依賴 配置 WebSocketServer.java 前端頁面 websocket.html 前端邏輯 index.js 參

    2024年02月04日
    瀏覽(29)
  • springboot集成webstock實戰(zhàn):服務(wù)端數(shù)據(jù)推送數(shù)據(jù)到客戶端實現(xiàn)實時刷新

    springboot集成webstock實戰(zhàn):服務(wù)端數(shù)據(jù)推送數(shù)據(jù)到客戶端實現(xiàn)實時刷新

    ????之前介紹過springboot集成webstock方式,具體參考: springboot集成websocket實戰(zhàn):站內(nèi)消息實時推送 這里補(bǔ)充另外一個使用webstock的場景,方便其他同學(xué)理解和使用,廢話不多說了,直接開始!簡單介紹一下業(yè)務(wù)場景: ????現(xiàn)在有一個投票活動,活動詳情中會顯示投票活動的參與人數(shù)、訪

    2024年02月08日
    瀏覽(48)
  • SpringBoot集成Milo庫實現(xiàn)OPC UA客戶端:連接、遍歷節(jié)點、讀取、寫入、訂閱與批量訂閱

    SpringBoot集成Milo庫實現(xiàn)OPC UA客戶端:連接、遍歷節(jié)點、讀取、寫入、訂閱與批量訂閱

    前面我們搭建了一個本地的 PLC 仿真環(huán)境,并通過 KEPServerEX6 讀取 PLC 上的數(shù)據(jù),最后還使用 UAExpert 作為OPC客戶端完成從 KEPServerEX6 這個OPC服務(wù)器的數(shù)據(jù)讀取與訂閱功能。在這篇文章中,我們將通過 SpringBoot 集成 Milo 庫實現(xiàn)一個 OPC UA 客戶端,包括連接、遍歷節(jié)點、讀取、寫入

    2024年02月09日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包