Springboot+websocket實現服務端、客戶端

2022-01-01 java版web項目
一、引言

小編最近一直在使用springboot框架開發項目,畢竟現在很多公司都在採用此框架,之後小編也會陸續寫關於springboot開發常用功能的文章。

什麼場景下會要使用到websocket的呢?

websocket主要功能就是實現網絡通訊,比如說最經典的客服聊天窗口、您有新的消息通知,或者是項目與項目之間的通訊,都可以採用websocket來實現。

二、websocket介紹

百度百科介紹:WebSokcet

在公司實際使用websocket開發,一般來都是這樣的架構,首先websocket服務端是一個單獨的項目,其他需要通訊的項目都是以客戶端來連接,由服務端控制消息的發送方式(群發、指定發送)。但是也會有服務端、客戶端在同一個項目當中,具體看項目怎麼使用。

本文呢,採用的是服務端與客戶端分離來實現,包括使用springboot搭建websokcet服務端、html5客戶端、springboot後臺客戶端, 具體看下面代碼。

三、服務端實現

*步驟一*:springboot底層幫我們自動配置了websokcet,引入maven依賴

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

*步驟二*:如果是你採用springboot內置容器啟動項目的,則需要配置一個Bean。如果是採用外部的容器,則可以不需要配置。

/**

 * @Auther: liaoshiyao
 * @Date: 2019/1/11 11:49
 * @Description: 配置類
 */
@Component
public class WebSocketConfig {


    /**
     * ServerEndpointExporter 作用
     *
     * 這個Bean會自動註冊使用@ServerEndpoint註解聲明的websocket endpoint
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

*步驟三*:最後一步當然是編寫服務端核心代碼了,其實小編不是特別想貼代碼出來,貼很多代碼影響文章可讀性。

package com.example.socket.code;


import lombok.extern.slf4j.Slf4j;
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.concurrent.ConcurrentHashMap;


/**
 * @Auther: liaoshiyao
 * @Date: 2019/1/11 11:48
 * @Description: websocket 服務類
 */

/**



 *
 * @ServerEndpoint 這個註解有什麼作用?
 *
 * 這個註解用於標識作用在類上,它的主要功能是把當前類標識成一個WebSocket的服務端
 * 註解的值用戶客戶端連接訪問的URL地址
 *
 */


@Slf4j
@Component
@ServerEndpoint("/websocket/{name}")
public class WebSocket {


    /**
     *  與某個客戶端的連接對話,需要通過它來給客戶端發送消息
     */
    private Session session;
     /**
     * 標識當前連接客戶端的用戶名
     */
    private String name;


    /**
     *  用於存所有的連接服務的客戶端,這個對象存儲是安全的
     */
    private static ConcurrentHashMap<String,WebSocket> webSocketSet = new ConcurrentHashMap<>();

    @OnOpen
    public void OnOpen(Session session, @PathParam(value = "name") String name){

        this.session = session;
        this.name = name;
        // name是用來表示唯一客戶端,如果需要指定發送,需要指定發送通過name來區分
        webSocketSet.put(name,this);

        log.info("[WebSocket] 連接成功,當前連接人數為:={}",webSocketSet.size());
    }




    @OnClose
    public void OnClose(){
        webSocketSet.remove(this.name);
        log.info("[WebSocket] 退出成功,當前連接人數為:={}",webSocketSet.size());
    }



    @OnMessage
    public void OnMessage(String message){
        log.info("[WebSocket] 收到消息:{}",message);

        //判斷是否需要指定發送,具體規則自定義
        if(message.indexOf("TOUSER") == 0){

            String name = message.substring(message.indexOf("TOUSER")+6,message.indexOf(";"));
            AppointSending(name,message.substring(message.indexOf(";")+1,message.length()));

        }else{

            GroupSending(message);
        }

    }


    /**
     * 群發
     * @param message
     */
    public void GroupSending(String message){
        for (String name : webSocketSet.keySet()){
            try {
                webSocketSet.get(name).session.getBasicRemote().sendText(message);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }



    /**
     * 指定發送
     * @param name
     * @param message
     */
    public void AppointSending(String name,String message){
        try {
            webSocketSet.get(name).session.getBasicRemote().sendText(message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

四、客戶端實現

*HTML5實現*:以下就是核心代碼了,其實其他博客有很多,小編就不多說了。

 var websocket = null;

    if('WebSocket' in window){
        websocket = new WebSocket("ws://192.168.2.107:8085/websocket/testname");
    }

    websocket.onopen = function(){
        console.log("連接成功");
    }


    websocket.onclose = function(){
        console.log("退出連接");
    }


    websocket.onmessage = function (event){
        console.log("收到消息"+event.data);
    }


    websocket.onerror = function(){
        console.log("連接出錯");
    }

    window.onbeforeunload = function () {
        websocket.close(num);
    }

*SpringBoot後臺實現*:小編發現多數博客都是採用js來實現客戶端,很少有用後臺來實現,所以小編也就寫了寫,大神請勿噴?。很多時候,項目與項目之間通訊也需要後臺作為客戶端來連接。

*步驟一*:首先我們要導入後臺連接websocket的客戶端依賴

<!--websocket作為客戶端-->

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.3.5</version>
</dependency>

*步驟二*:把客戶端需要配置到springboot容器裡面去,以便程序調用。

package com.example.socket.config;

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.net.URI;

/**
 * @Auther: liaoshiyao
 * @Date: 2019/1/11 17:38
 * @Description: 配置websocket後臺客戶端
 */
@Slf4j
@Component
public class WebSocketConfig {
    @Bean
    public WebSocketClient webSocketClient() {
        try {
            WebSocketClient webSocketClient = new WebSocketClient(new URI("ws://localhost:8085/websocket/test"),new Draft_6455()) {

                @Override
                public void onOpen(ServerHandshake handshakedata) {
                    log.info("[websocket] 連接成功");
                }


                @Override
                public void onMessage(String message) {
                    log.info("[websocket] 收到消息={}",message);

                }


                @Override
                public void onClose(int code, String reason, boolean remote) {
                    log.info("[websocket] 退出連接");
                }


                @Override
                public void onError(Exception ex) {
                    log.info("[websocket] 連接錯誤={}",ex.getMessage());
                }
            };

            webSocketClient.connect();
            return webSocketClient;

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;
    }


}

*步驟三*:使用後臺客戶端發送消息

1、首先小編寫了一個接口,裡面有指定發送和群發消息兩個方法。

2、實現發送的接口,區分指定發送和群發由服務端來決定(小編在服務端寫了,如果帶有TOUSER標識的,則代表需要指定發送給某個websocket客戶端)

3、最後採用get方式用瀏覽器請求,也能正常發送消息

package com.example.socket.code;
/**
 * @Auther: liaoshiyao
 * @Date: 2019/1/12 10:57
 * @Description: websocket 接口
 */
public interface WebSocketService {

    /**
     * 群發
     * @param message
     */
     void groupSending(String message);

    /**
     * 指定發送
     * @param name
     * @param message
     */
     void appointSending(String name,String message);
}
package com.example.socket.code;

import org.java_websocket.client.WebSocketClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * @Auther: liaoshiyao
 * @Date: 2019/1/12 10:56
 * @Description: websocket接口實現類
 */
@Component
public class ScoketClient implements WebSocketService{

    @Autowired
    private WebSocketClient webSocketClient;

    @Override
    public void groupSending(String message) {

        // 這裡我加了6666-- 是因為我在index.html頁面中,要拆分用戶編號和消息的標識,只是一個例子而已
        // 在index.html會隨機生成用戶編號,這裡相當於模擬頁面發送消息
        // 實際這樣寫就行了 webSocketClient.send(message)
        webSocketClient.send(message+"---6666");
    }

    @Override
    public void appointSending(String name, String message) {

        // 這裡指定發送的規則由服務端決定參數格式
        webSocketClient.send("TOUSER"+name+";"+message);

    }
}
package com.example.socket.chat;

import com.example.socket.code.ScoketClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Auther: liaoshiyao
 * @Date: 2019/1/11 16:47
 * @Description: 測試後臺websocket客戶端

 */
@RestController
@RequestMapping("/websocket")
public class IndexController {


    @Autowired
    private ScoketClient webScoketClient;


    @GetMapping("/sendMessage")
    public String sendMessage(String message){
        webScoketClient.groupSending(message);
        return message;
    }
}

五、最後

其實,貼了這麼多大一片的代碼,感覺上影響了博客的美觀,也不便於瀏覽,如果沒看懂小夥伴,可以下載源碼看下。

裡面一共兩個項目,服務端、客戶端(html5客戶端、後臺客戶端),是一個網頁群聊的小案例。

https://download.csdn.net/download/weixin_38111957/10912384

*祝大家學習愉快~~~*

六、針對評論區的小夥伴提出的疑點進行解答

看了小夥伴提出的疑問,小編也是非常認可的,如果是單例的情況下,這個對象的值都會被修改。

小編就抽了時間Debug了一下,經過下圖也可以反映出,能夠看出,webSokcetSet中存在三個成員,並且vlaue值都是不同的,所以在這裡沒有出現對象改變而把之前對象改變的現象。

服務端這樣寫是沒問題的。

緊接著,小編寫了一個測試類,代碼如下,經過測試輸出的結果和小夥伴提出的疑點是一致的。

最後總結:這位小夥伴提出的觀點確實是正確的,但是在實際WebSocket服務端案例中為什麼沒有出現這種情況,當WebSokcet這個類標識為服務端的時候,每當有新的連接請求,這個類都是不同的對象,並非單例。

這裡也感謝「煙花蘇柳」所提出的問題。

import com.alibaba.fastjson.JSON;
import java.util.concurrent.ConcurrentHashMap;

/**

 * @Auther: IT賤男

 * @Date: 2018/11/1 16:15

 * @Description:

 */

public class TestMain {

    /**

     * 用於存所有的連接服務的客戶端,這個對象存儲是安全的

     */

    private static ConcurrentHashMap<String, Student> webSocketSet = new ConcurrentHashMap<>();


    public static void main(String[] args) {

        Student student = Student.getStudent();

        student.name = "張三";

        webSocketSet.put("1", student);

        Student students = Student.getStudent();
        students.name = "李四";

        webSocketSet.put("2", students);

        System.out.println(JSON.toJSON(webSocketSet));
    }
}

/**

 * 提供一個單例類

 */

class Student {

    public String name;

    private Student() {

    }

    private static final Student student = new Student();


    public static Student getStudent() {
        return student;
    }

}

{"1":{"name":"李四"},"2":{"name":"李四"}}

點擊閱讀原文,前往學習SpringCloud實戰項目

相關焦點

  • SpringBoot+WebSocket實現簡單的數據推送
    websocket相對於傳統http協議有什麼優勢呢?websocket響應報文好了,簡單的了解一下,我們來看看springboot+websocket實現推送的過程,以下是個簡單的demo。try { session.getBasicRemote().sendText(toJson); } catch (IOException e) { LOGGER.error("發送錯誤:{}",e.getMessage()); } } }服務端
  • SpringBoot + Vue + WebSocket 實時通信
    public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; }}下面主要是為了解決通過服務端向客戶端傳遞一個對象的問題
  • Springboot整合websocket實現一對一消息推送和廣播消息推送
    基礎環境請參考springboot文檔maven依賴        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-websocket
  • WebSocket實現Web端即時通信
    WebSocketWebSocket在客戶端和服務端只需一次請求,就會在客戶端和服務端建立一條通信通道,可以實時相互傳輸數據,並且不會像HTTP那樣攜帶大量請求頭等信息。因為WebSocket是基於TCP雙向全雙工通信的協議,所以支持在同一時間點處理髮送和接收消息,做到實時的消息處理。
  • 在 go 中實現 websocket 服務
    目標:了解 websocket ,能夠使用 golang 來實現 websocket 服務要求:了解 go 基本語法
  • 基於springBoot 實現webSocket方式的掃碼登錄
    所以這次用 webSocket 的方式進行實現好。廢話不多說!咱們開始!!一、首先咱們需要一張表這表是幹啥的呢?就是記錄一下誰掃碼了。誰登錄了。掃碼登錄這個業務邏輯都有哪些角色1、android 端 or 微信 Web 端 :掃碼2、PC 端 :被掃。登錄3、服務端:掌控全局,提供接口。三、接口都需要哪些?有了角色。你用大腿也能想出來接口了對不對!!所以咱們的接口有 2 個!1、生成二維碼接口:生成一個二維碼。二維碼中有 UUID。
  • spring-boot之webSocket · 上
    它實現了瀏覽器與伺服器全雙工( full-duplex )通信一一允許伺服器主動發送信息給客戶端,這樣就可以實現從客戶端發送消息到伺服器 ,而伺服器又可以轉發消息到客戶端,這樣就能夠實現客戶端之間的交互。對於WebSocket的 開發 ,Spring也提供了 良好 的支持 。
  • 使用spring boot+WebSocket 實現定時消息推送(基於註解)
    第一這篇文章需求業務場景有些不同,第二這篇文章 websocket 基本上完全基於註解操作簡單。其實能實現定時消息推送的技術有很多,Dwr、goeasy、comer4j 、netPush 等技術也可以完全實現這個功能.
  • SpringBoot+webSocket實現掃碼登錄功能
    PC端打開。調用生成二維碼接口 並與 服務端建立連結。連結使用uuid進行綁定微信Web端拿到uuid以後。顯示是否登錄頁面。點擊確定後 調用 確認身份接口。確認身份接口通過以後。服務端給PC端發送信息。完成登錄。此時連結斷開。好了!分析完了這些。你們一定在想。。還有完沒完啊。。不要在BB了。。趕緊貼代碼吧。。
  • 基於 Netty 搭建 WebSocket 集群實現伺服器消息推送
    目錄為:4、springboot環境下使用netty搭建websocket我們在實際的工作開發中,必然會遇到一些需要網頁與伺服器端保持連接(起碼看上去是保持連接)的需求,比如類似微信網頁版的聊天類應用,比如需要頻繁更新頁面數據(實時數據例如天氣,電流電壓,pm2.5等等這樣類似的數據)的監控系統頁面或股票看盤頁面,比如伺服器讀取MySQL或者redis或者第三方的數據主動推送給瀏覽器客戶端等等業務場景
  • SpringBoot集成WebSocket,實現後臺向前端推送信息
    一、什麼是websocket?WebSocket協議是基於TCP的一種新的網絡協議。它實現了客戶端與伺服器全雙工通信,學過計算機網絡都知道,既然是全雙工,就說明了伺服器可以主動發送信息給客戶端。這與我們的推送技術或者是多人在線聊天的功能不謀而合。
  • SpringBoot 集成 WebSocket,實現後臺向前端推送信息
    作者 | 大樹先生
  • [WebSocket入門]手把手搭建WebSocket多人在線聊天室(SpringBoot+WebSocket)
    此外,在下一篇文章中,我們將做到:正文WebSocket多人在線聊天室本文工程原始碼:https://github.com/qqxx6661/springboot-websocket-demo我們實現了WebSocketMessageBrokerConfigurer接口,並實現了其中的方法。在第一種方法中,我們註冊一個websocket端點,客戶端將使用它連接到我們的websocket伺服器。withSockJS()是用來為不支持websocket的瀏覽器啟用後備選項,使用了SockJS。
  • 終於實現了 SpringBoot+WebSocket實時監控異常....
    來自:cnblogs.com/jae-tech/p/15409340.html最近做了一個需求,消防的設備巡檢,如果巡檢發現異常,通過手機端提交
  • C#實現WebSocket協議客戶端和伺服器Websocket-Sharp組件解析
    一.Websocket-sharp組件概述Websocket-sharp是一個C#實現websocket協議客戶端和服務端,websocket-sharp支持RFC 6455;WebSocket客戶端和伺服器;消息壓縮擴展;安全連接;HTTP身份驗證;查詢字符串,起始標題和Cookie;通過HTTP代理伺服器連接;.NET Framework
  • 太贊了,竟然用SpringBoot打造一款網頁版的IM,進行聊天...
    對於Java 開發者而言,後臺 WebSocket 服務端開發通常有以下常用的選擇:Tomcat8 如下所示,自身已經支持 WebSocket 服務端開發,它的 lib 目錄下有自己實現 WebSocket 協議的開發包,如果是傳統的 Java Web 項目,則只需要將 tomcat-websocket.jar、websocket-api.jar 導入應用中即可進行代碼開發。
  • [經驗漫談]構建websocket伺服器和客戶端
    tornado自帶websocket伺服器和客戶端的功能,可以拿來即用,十分方便。我曾經還使用過websockets這個包,本以為專門做websocket的工具會更強大一些,但是速度比較慢,不知道是我代碼的問題還是其他什麼原因。本文就簡單給出一個tornado實現的範例,供讀者參考。
  • Python實現一個最簡單websocket服務!
    這裡的客戶端和服務端通常是用戶瀏覽器和 Web 伺服器。在 WebSocket 誕生之前,如果我們想保持這樣的一個長連接,就需要使用諸如長輪詢、永久幀、Comet 等技術。而現今 WebSocket 已經得到了所有主流瀏覽器的支持,我們可以使用它開發出在線聊天室、遊戲、實時儀錶盤等軟體。
  • SpringBoot集成WebSocket,實現後臺向前端推送信息【文末送書】
    一、什麼是websocket?WebSocket協議是基於TCP的一種新的網絡協議。它實現了客戶端與伺服器全雙工通信,學過計算機網絡都知道,既然是全雙工,就說明了伺服器可以主動發送信息給客戶端。這與我們的推送技術或者是多人在線聊天的功能不謀而合。為什麼不使用HTTP 協議呢?
  • 分布式WebSocket集群解決方案
    | 解決方案的演變Netty與Spring WebSocket剛開始的時候,我嘗試著用netty實現了websocket服務端的搭建。與websocket session類似的,服務端在連接建立後有一個channel,我們可以通過channel進行與客戶端的通信   /**    * TODO 根據伺服器傳進來的id,分配到不同的group    */   private static final ChannelGroup GROUP = new