SpringBoot+WebSocket實現服務端、客戶端

2020-09-11 佳雲大腦

作者 | 47號Gamer丶

來源 | urlify.cn/jUR3Af

66套java從入門到精通實戰課程分享

一、引言

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

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

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

二、websocket介紹

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

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

三、服務端實現

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

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

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

/** * @Description: 配置類 */@Componentpublic 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;  /** * @Description: websocket 服務類 */  /** * * @ServerEndpoint 這個註解有什麼作用? * * 這個註解用於標識作用在類上,它的主要功能是把當前類標識成一個WebSocket的服務端 * 註解的值用戶客戶端連接訪問的URL地址 * */  @Slf4j@Component@ServerEndpoint(&34;)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 = &34;) String name){        this.session = session;        this.name = name;        // name是用來表示唯一客戶端,如果需要指定發送,需要指定發送通過name來區分        webSocketSet.put(name,this);        log.info(&34;,webSocketSet.size());    }        @OnClose    public void OnClose(){        webSocketSet.remove(this.name);        log.info(&34;,webSocketSet.size());    }      @OnMessage    public void OnMessage(String message){        log.info(&34;,message);        //判斷是否需要指定發送,具體規則自定義        if(message.indexOf(&34;) == 0){            String name = message.substring(message.indexOf(&34;)+6,message.indexOf(&34;));            AppointSending(name,message.substring(message.indexOf(&34;)+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(&39; in window){       websocket = new WebSocket(&34;);   }    websocket.onopen = function(){       console.log(&34;);   }    websocket.onclose = function(){       console.log(&34;);   }    websocket.onmessage = function (event){       console.log(&34;+event.data);   }    websocket.onerror = function(){       console.log(&34;);   }    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;  /** * @Description: 配置websocket後臺客戶端 */@Slf4j@Componentpublic class WebSocketConfig {      @Bean    public WebSocketClient webSocketClient() {        try {            WebSocketClient webSocketClient = new WebSocketClient(new URI(&34;),new Draft_6455()) {                @Override                public void onOpen(ServerHandshake handshakedata) {                    log.info(&34;);                }                  @Override                public void onMessage(String message) {                    log.info(&34;,message);                  }                  @Override                public void onClose(int code, String reason, boolean remote) {                    log.info(&34;);                }                  @Override                public void onError(Exception ex) {                    log.info(&34;,ex.getMessage());                }            };            webSocketClient.connect();            return webSocketClient;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }  }

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

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

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

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

package com.example.socket.code;  /** * @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.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;  /** * @Description: 測試後臺websocket客戶端 */@RestController@RequestMapping(&34;)public class IndexController {      @Autowired    private ScoketClient webScoketClient;      @GetMapping(&34;)    public String sendMessage(String message){        webScoketClient.groupSending(message);        return message;    }}

五、最後

注意:

如果是單例的情況下,這個對象的值都會被修改。

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

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

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

import com.alibaba.fastjson.JSON;  import java.util.concurrent.ConcurrentHashMap;  /** * @Description: */public class TestMain {      /**     * 用於存所有的連接服務的客戶端,這個對象存儲是安全的     */    private static ConcurrentHashMap<String, Student> webSocketSet = new ConcurrentHashMap<>();      public static void main(String[] args) {        Student student = Student.getStudent();        student.name = &34;;        webSocketSet.put(&34;, student);          Student students = Student.getStudent();        students.name = &34;;        webSocketSet.put(&34;, 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;      }}

列印結果:

{&34;:{&34;:&34;},&34;:{&34;:&34;}}




相關焦點

  • SpringBoot+WebSocket實現簡單的數據推送
    問題背景為什麼要要用websocket呢?websocket相對於傳統http協議有什麼優勢呢?http協議有一個缺陷,就是通信只能由客戶端發起,伺服器返回數據,不能做到伺服器主動向客戶端推送。websocket使用tcp連接保持全雙工通信協議,伺服器端比較容易實現;數據格式比較輕量,通信比較高效;既能發送文本,也能發送二進位數據,沒有同源限制;wbsocket響應報文與http不同,如下
  • 使用websocket從服務端給客戶端發消息
    效果圖一般情況下都是瀏覽器發送請求給伺服器,然後服務端返回消息給客戶端。如果需要服務端主動給瀏覽器發送一波數據,可以使用websocket方式。首先讓服務端支持websocket連接,然後就是前端也要支持websocket連接,前端頁面要與伺服器進行websocket連接後才能相互發送消息。websocket使用ws或wss協議,對應http或https協議。如網頁聊天。添加pom依賴。
  • java使用springboot創建一個webSocket項目,服務端如何通知瀏覽器...
    @ServerEndpoint("/hall/{nickName}")@Componentpublic class Hall { //當前在線人數 private static int onlineCount = 0; //每個客戶端對應的session
  • netty-socketio+vue實現服務端推送消息到前端
    這種時候需要服務端主動向用戶推送一些內容,一般有兩種做法:客戶端不斷的請求服務端以獲取最新數據(輪詢)服務端主動推送給客戶端(消息通知/推送)一般來講,第二種會更加優雅一些,並且減少了大量的請求消耗。市場上也有很多的消息推送技術,比如websocket、socketio、netty等。
  • SpringBoot項目中websocket報錯問題
    介紹 系統使用springboot開發,用到了websocket推送消息到頁面,但是登陸系統後,報錯信息如下:WebSocket connection to &39; failed: Error during WebSocket handshake: Unexpected response code: 404.
  • SpringBoot項目中websocket報錯問題
    介紹系統使用springboot開發,用到了websocket推送消息到頁面,但是登陸系統後,報錯信息如下:WebSocket connection to 'ws://192.168.1.141:8089/finesys_user/lehoon' failed: Error during
  • websocket無法連接swoole開啟的https服務端
    swoole中連接https問題前段時間有夥伴諮詢一點PHP博主關於websocket連接HTTPS的問題,網上資料又非常雜亂,所以博主也親自嘗試了一下一些問題以及技術總結分享給大家。服務端使用的是PHP擴展Swoole框架開發,客戶端採用JS中的websocket開發。HTTPS解決方案如下:1.編譯安裝swoole時記住選擇支持openssl。cd swoole_srcphpize .
  • 太贊了,竟然用SpringBoot打造一款網頁版的IM,進行聊天
    自身已經支持 WebSocket 服務端開發,它的 lib 目錄下有自己實現 WebSocket 協議的開發包,如果是傳統的 Java Web 項目,則只需要將 tomcat-websocket.jar、websocket-api.jar 導入應用中即可進行代碼開發。
  • Uniapp使用GoEasy實現websocket實時通訊
    比如:1、GoEasy和Uniapp websocket API有什麼區別和優勢?Uniapp官方的websocket API主要是用來與您的websocket服務通訊,所以使用Uniapp websocket的前提是,首先要搭建好您自己的websocket服務,然後與之通訊。
  • Springboot stomp websocket使用應注意的地方
    上下文中添加,location指用於websocket連接的path。#34;ws-heartbeat-thread-");te.initialize();registry.enableSimpleBroker( "/topic") .setHeartbeatValue(new long[]{HEART_BEAT,HEART_BEAT}).setTaskScheduler(te);建立連接之後顯示的客戶端和服務端心跳的信息如下
  • 基於Swoole搭建WebSocket服務
    在命令行執行如下命令即可開啟一個 WebSocket 服務,命令如下:php ws_websocket.php可以在 Chrome 瀏覽器進行測試,JS代碼如下:在 Chrome 瀏覽器的控制臺執行上面的 js 代碼,即可看到如下的輸出結果
  • 使用Springboot啟動web-socket服務端,並且使用nodejs測試
    首先,定義連接地址endPoint,以及客戶端和服務端通訊的topic,分為廣播和p2p兩種。"表示客戶端連接服務端的地址,比如:"http://localhost:8081/gs-guide-websocket" registry.addEndpoint("/gs-guide-websocket") .setAllowedOrigins("*") .withSockJS() .setHeartbeatTime(30000); } @Override public void configureMessageBroker
  • SpringBoot+Netty+Websocket整合案例(實現基本的聊天功能)
    之前使用Springboot整合了websocket,實現了一個後端向前端推送信息的基本小案例,這篇文章主要是增加了一個新的框架就是Netty,實現一個高性能的websocket伺服器,並結合前端代碼,實現一個基本的聊天功能。你可以根據自己的業務需求進行更改。
  • SpringBoot+WebSocket實現廣播和點對點推送
    void queue(ReveiceMsg reveiceMsg) { // 這裡指定發送給zs messagingTemplate.convertAndSendToUser("zs","/message",reveiceMsg.getName()); }}3.客戶端廣播頁面
  • WebSocket實現原理
    原因是我們服務端在做改造,同時網頁版 IM 已經使用了 WebSocket ,客戶端也採用的話對於服務端來說維護一套代碼會更好更方便,而且 WebSocket 在體積、實時性和擴展上都具有一定的優勢。 WebSocket 最新的協議是 13 RFC 6455 ,要理解 WebSocket 的實現,一定要去理解它的協議!
  • Netty核心14-簡易實現websocket
    前面三篇講解了如何用netty來實現http伺服器。本篇來講解下如何用netty來做websocket伺服器。關於websocket的知識點我這裡過多不講解,大家可以自行百度。Websocket之間的通信有兩個環節。
  • SpringBoot+Cas(三) Cas客戶端 開發
    客戶端開發:SpringBoot整合Cas客戶端1springboot_cas_01客戶端2springboot_cas_02springboot_cas_01 項目 開發 808080; --tt-darkmode-color: cas 伺服器的配置(配置的是cas伺服器的埠號) cas: 808080; --tt-darkmode-color: cas伺服器端的地址
  • Qt封裝的websocket了解一下
    第一次看到websocket這個詞時很難想到它竟然會是一種協議。據說是一種全雙工通訊協議,伺服器也可以主動向客戶端發送數據,解決了Http協議一問一答式帶來的效率問題。不過這不是本文關注的重點,這裡關心的是利用webscoket可以實現網頁與Qt寫的服務端通信。
  • 搭建websocket消息推送服務,必須要考慮的幾個問題
    因為在websocket長連接中,客戶端和服務端並不會一直通信,如果雙方長期沒有溝通則都不清楚彼此當前狀態,所以需要發送一段很小的報文告訴對方「我還活著」。另外還有兩個目的:服務端檢測到某個客戶端遲遲沒有心跳過來可以主動關閉通道,讓它下線;客戶端檢測到某個服務端遲遲沒有響應心跳也能重連獲取一個新的連接。
  • WebSocket 的原理
    web 應用,需要依賴HTTP協議,進行不停的輪詢,這會導致一些問題:服務端被迫維持來自每個客戶端的大量不同的連接大量的輪詢請求會造成高開銷,比如會帶上多餘的header,造成了無用的數據傳輸。WebSocket 使得客戶端和伺服器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以創建持久性的連接, 並進行雙向數據傳輸。