日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

SpringBoot使用WebSocket實現即時消息

來源: 責編: 時間:2023-08-14 22:01:09 345觀看
導讀環境:SpringBoot2.4.12.依賴<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupI

環境:SpringBoot2.4.12.c6s28資訊網——每日最新資訊28at.com


依賴

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

定義消息類型

  • 抽象消息對象
public class AbstractMessage {  /**   *   消息類型   */  protected String type ;    /**   *   消息內容   */  protected String content ;  /**   *   消息日期   */  protected String date ;}

消息對象子類c6s28資訊網——每日最新資訊28at.com

1、Ping檢查消息c6s28資訊網——每日最新資訊28at.com

public class PingMessage extends AbstractMessage {  public PingMessage() {}  public PingMessage(String type) {    this.type = type ;  }}

2、系統消息c6s28資訊網——每日最新資訊28at.com

public class SystemMessage extends AbstractMessage {  public SystemMessage() {}  public SystemMessage(String type, String content) {    this.type = type ;    this.content = content ;  }}

3、點對點消息c6s28資訊網——每日最新資訊28at.com

public class PersonMessage extends AbstractMessage {  private String fromName ;  private String toName ;}

消息類型定義c6s28資訊網——每日最新資訊28at.com

public enum MessageType {    /**   *   系統消息 0000;心跳檢查消息 0001;點對點消息2001   */  SYSTEM("0000"), PING("0001"), PERSON("2001") ;    private String type ;    private MessageType(String type) {    this.type = type ;  }  public String getType() {    return type;  }  public void setType(String type) {    this.type = type;  }  }

WebSocket服務端點

該類作用就是定義客戶端連接的地址c6s28資訊網——每日最新資訊28at.com

@ServerEndpoint(value = "/message/{username}",   encoders = {WsMessageEncoder.class},  decoders = {WsMessageDecoder.class},  subprotocols = {"gmsg"},  configurator = MessageConfigurator.class)  @Component  public class GMessageListener {        public static ConcurrentMap<String, UserSession> sessions = new ConcurrentHashMap<>();    private static Logger logger = LoggerFactory.getLogger(GMessageListener.class) ;      private String username ;        @OnOpen      public void onOpen(Session session, EndpointConfig config, @PathParam("username") String username){      UserSession userSession = new UserSession(session.getId(), username, session) ;      this.username = username ;      sessions.put(username, userSession) ;      logger.info("【{}】用戶進入, 當前連接數:{}", username, sessions.size()) ;     }        @OnClose      public void onClose(Session session, CloseReason reason){        UserSession userSession = sessions.remove(this.username) ;      if (userSession != null) {        logger.info("用戶【{}】, 斷開連接, 當前連接數:{}", username, sessions.size()) ;      }    }        @OnMessage    public void pongMessage(Session session, PongMessage message) {      ByteBuffer buffer = message.getApplicationData() ;      logger.debug("接受到Pong幀【這是由瀏覽器發送】:" + buffer.toString());    }        @OnMessage    public void onMessage(Session session, AbstractMessage message) {      if (message instanceof PingMessage) {        logger.debug("這里是ping消息");        return ;      }      if (message instanceof PersonMessage) {        PersonMessage personMessage = (PersonMessage) message ;        if (this.username.equals(personMessage.getToName())) {          logger.info("【{}】收到消息:{}", this.username, personMessage.getContent());        } else {          UserSession userSession = sessions.get(personMessage.getToName()) ;          if (userSession != null) {            try {            userSession.getSession().getAsyncRemote().sendText(new ObjectMapper().writeValueAsString(message)) ;          } catch (JsonProcessingException e) {            e.printStackTrace();          }          }        }        return ;      }      if (message instanceof SystemMessage) {        logger.info("接受到消息類型為【系統消息】") ;         return ;      }    }        @OnError    public void onError(Session session, Throwable error) {      logger.error(error.getMessage()) ;    }}

WsMessageEncoder.java類
該類的主要作用是,當發送的消息是對象時,該如何轉換c6s28資訊網——每日最新資訊28at.com

public class WsMessageEncoder implements Encoder.Text<AbstractMessage> {  private static Logger logger = LoggerFactory.getLogger(WsMessageDecoder.class) ;  @Override  public void init(EndpointConfig endpointConfig) {  }  @Override  public void destroy() {  }  @Override  public String encode(AbstractMessage tm) throws EncodeException {    String message = null ;    try {      message = new ObjectMapper().writeValueAsString(tm);    } catch (JsonProcessingException e) {      logger.error("JSON處理錯誤:{}", e) ;    }    return message;  }}

WsMessageDecoder.java類
該類的作用是,當接收到消息時如何轉換成對象。c6s28資訊網——每日最新資訊28at.com

public class WsMessageDecoder implements  Decoder.Text<AbstractMessage> {  private static Logger logger = LoggerFactory.getLogger(WsMessageDecoder.class) ;  private static Set<String> msgTypes = new HashSet<>() ;    static {    msgTypes.add(MessageType.PING.getType()) ;    msgTypes.add(MessageType.SYSTEM.getType()) ;    msgTypes.add(MessageType.PERSON.getType()) ;  }  @Override  @SuppressWarnings("unchecked")  public AbstractMessage decode(String s) throws DecodeException {    AbstractMessage message = null ;    try {      ObjectMapper mapper = new ObjectMapper() ;      Map<String,String> map = mapper.readValue(s, Map.class) ;      String type = map.get("type") ;      switch(type) {        case "0000":          message = mapper.readValue(s, SystemMessage.class) ;          break;        case "0001":          message = mapper.readValue(s, PingMessage.class) ;          break;        case "2001":          message = mapper.readValue(s, PersonMessage.class) ;          break;      }    } catch (JsonProcessingException e) {      logger.error("JSON處理錯誤:{}", e) ;    }    return message ;  }  // 該方法判斷消息是否可以被解碼(轉換)  @Override  @SuppressWarnings("unchecked")  public boolean willDecode(String s) {    Map<String, String> map = new HashMap<>() ;    try {      map = new ObjectMapper().readValue(s, Map.class);    } catch (JsonProcessingException e) {      e.printStackTrace();    }    logger.debug("檢查消息:【" + s + "】是否可以解碼") ;    String type = map.get("type") ;    if (StringUtils.isEmpty(type) || !msgTypes.contains(type)) {      return false ;    }    return true ;  }  @Override  public void init(EndpointConfig endpointConfig) {  }  @Override  public void destroy() {  }}

MessageConfigurator.java類
該類的作用是配置服務端點,比如配置握手信息c6s28資訊網——每日最新資訊28at.com

public class MessageConfigurator extends ServerEndpointConfig.Configurator {  private static Logger logger = LoggerFactory.getLogger(MessageConfigurator.class) ;  @Override  public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {    logger.debug("握手請求頭信息:" + request.getHeaders());    logger.debug("握手響應頭信息:" + response.getHeaders());    super.modifyHandshake(sec, request, response);  }  }

WebSocke配置類

@Configurationpublic class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter (){          return new ServerEndpointExporter();      }    }

當以jar包形式運行時需要配置該bean,暴露我們配置的@ServerEndpoint;當我們以war獨立tomcat運行時不能配置該bean。c6s28資訊網——每日最新資訊28at.com

前端頁面

<!doctype html><html> <head>  <meta charset="UTF-8">  <meta name="Author" content="">  <meta name="Keywords" content="">  <meta name="Description" content="">  <script src="g-messages.js?v=1"></script>  <title>WebSocket</title>  <style type="text/css"></style>  <script>  let gm = null ;  let username = null ;  function ListenerMsg({url, protocols = ['gmsg'], options = {}}) {    if (!url){       throw new Error("未知服務地址") ;    }    gm = new window.__GM({      url: url,      protocols: protocols    }) ;    gm.open(options) ;  }  ListenerMsg.init = (user) => {    if (!user) {      alert("未知的當前登錄人") ;      return ;    }    let url = `ws://localhost:8080/message/${user}` ;    let msg = document.querySelector("#msg")    ListenerMsg({url, options: {      onmessage (e) {        let data = JSON.parse(e.data) ;        let li = document.createElement("li") ;        li.innerHTML = "【" + data.fromName + "】對你說:" + data.content ;        msg.appendChild(li) ;      }    }}) ;  }  function enter() {    username = document.querySelector("#nick").value ;    ListenerMsg.init(username) ;    document.querySelector("#chat").style.display = "block" ;    document.querySelector("#enter").style.display = "none" ;    document.querySelector("#cu").innerText = username ;  }  function send() {    let a = document.querySelector("#toname") ;    let b = document.querySelector("#content") ;    let toName = a.value ;    let content = b.value ;    gm.sendMessage({type: "2001", content, fromName: username, toName}) ;    a.value = '' ;    b.value = '' ;  }</script> </head> <body>  <id="enter">    <input id="nick"/><button type="button" onclick="enter()">進入</button>  </div>  <hr/>  <id="chat" style="display:none;">    當前用戶:<b id="cu"></b><br/>    用戶:<input id="toname" name="toname"/><br/><br/>    內容:<textarea id="content" rows="3" cols="22"></textarea><br/>    <button type="button" onclick="send()">發送</button>  </div>  <div>    <ul id="msg">    </ul>  </div> </body></html>

這里有個g-messages.js文件是我寫的一個工具類,用來做連接及心跳檢查用的。c6s28資訊網——每日最新資訊28at.com

到此所有的代碼完畢,接下來測試c6s28資訊網——每日最新資訊28at.com

測試

打開兩個標簽頁,以不同的用戶進入。c6s28資訊網——每日最新資訊28at.com

圖片c6s28資訊網——每日最新資訊28at.com


c6s28資訊網——每日最新資訊28at.com

圖片c6s28資訊網——每日最新資訊28at.com


c6s28資訊網——每日最新資訊28at.com

輸入對方用戶名發送消息c6s28資訊網——每日最新資訊28at.com

圖片c6s28資訊網——每日最新資訊28at.com


c6s28資訊網——每日最新資訊28at.com

圖片c6s28資訊網——每日最新資訊28at.com


c6s28資訊網——每日最新資訊28at.com

成功了,簡單的websocket。我們生產環境還就這么完的,8g內存跑了6w的用戶。c6s28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-5718-0.htmlSpringBoot使用WebSocket實現即時消息

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 停止過度設計中等規模的前端應用程序

下一篇: 提升Go的HTTP路由器的提案

標簽:
  • 熱門焦點
  • 一加Ace2 Pro真機揭曉 鈦空灰配色質感拉滿

    終于,在經過了幾波預熱之后,一加Ace2 Pro的外觀真機圖在網上出現了。還是博主數碼閑聊站曝光的,這次的外觀設計還是延續了一加11的方案,只是細節上有了調整,例如新加入了鈦空灰
  • 6月安卓手機性價比榜:Note 12 Turbo斷層式碾壓

    6月份有一個618,雖然這是京東周年慶的日子,但別的電商也都不約而同的跟進了,反正促銷沒壞處,廠商和用戶都能滿意。618期間一些產品也出現了歷史低價,那么各個價位段的產品性價比
  • 為什么你不應該使用Div作為可點擊元素

    按鈕是為任何網絡應用程序提供交互性的最常見方式。但我們經常傾向于使用其他HTML元素,如 div span 等作為 clickable 元素。但通過這樣做,我們錯過了許多內置瀏覽器的功能。
  • 2023年,我眼中的字節跳動

    此時此刻(2023年7月),字節跳動從未上市,也從未公布過任何官方的上市計劃;但是這并不妨礙它成為中國最受關注的互聯網公司之一。從2016-17年的抖音強勢崛起,到2018年的&ldquo;頭騰
  • Temu起訴SHEIN,跨境電商戰事升級

    來源 | 伯虎財經(bohuFN)作者 | 陳平安日前據外媒報道,拼多多旗下跨境電商平臺Temu正對競爭對手SHEIN提起新訴訟,訴狀稱Shein&ldquo;利用市場支配力量強迫服裝廠商與之簽訂獨家
  • 共享單車的故事講到哪了?

    來源丨海克財經與共享充電寶相差不多,共享單車已很久沒有被國內熱點新聞關照到了。除了一再漲價和用戶直呼用不起了。近日多家媒體再發報道稱,成都、天津、鄭州等地多個共享單
  • 自律,給不了Keep自由!

    來源 | 互聯網品牌官作者 | 李大為編排 | 又耳 審核 | 谷曉輝自律能不能給用戶自由暫時不好說,但大概率不能給Keep自由。近日,全球最大的在線健身平臺Keep正式登陸港交所,努力
  • 榮耀Magicbook V 14 2021曙光藍版本正式開售,擁有觸摸屏

    榮耀 Magicbook V 14 2021 曙光藍版本正式開售,搭載 i7-11390H 處理器與 MX450 顯卡,配備 16GB 內存與 512GB SSD,重 1.48kg,厚 14.5mm,具有 1.5mm 鍵盤鍵程、
  • 電博會與軟博會實現"線下+云端"的雙線融合

    在本次“電博會”與“軟博會”雙展會利好條件的加持下,既可以發揮展會拉動人流、信息流、資金流實現快速交互流動的作用,繼而推動區域經濟良性發展;又可以聚
Top 主站蜘蛛池模板: 永宁县| 梁河县| 永嘉县| 赞皇县| 莱阳市| 博罗县| 梁河县| 平顶山市| 玛曲县| 新化县| 宜春市| 扶风县| 商南县| 成都市| 乐陵市| 海城市| 西贡区| 威海市| 商南县| 宁夏| 石嘴山市| 乌兰察布市| 凤庆县| 阿坝| 无锡市| 济宁市| 罗江县| 湛江市| 丰都县| 临沧市| 郯城县| 西青区| 芷江| 香格里拉县| 彭阳县| 肇源县| 沙洋县| 安义县| 平定县| 泾阳县| 辽源市|