自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot使用WebSocket實現(xiàn)即時消息

開發(fā) 前端
本篇給大家介紹SpringBoot使用WebSocket實現(xiàn)即時消息,希望能夠幫助到你!

[[389469]]

 環(huán)境:SpringBoot2.3.9.RELEASE

依賴

  1. <dependency> 
  2.         <groupId>org.springframework.boot</groupId> 
  3.         <artifactId>spring-boot-starter-web</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6.         <groupId>org.springframework.boot</groupId> 
  7.         <artifactId>spring-boot-starter-websocket</artifactId> 
  8. </dependency> 

定義消息類型

抽象消息對象

  1. public class AbstractMessage { 
  2.     /** 
  3.      *  消息類型 
  4.      */ 
  5.     protected String type ; 
  6.      
  7.     /** 
  8.      *  消息內(nèi)容 
  9.      */ 
  10.     protected String content ; 
  11.     /** 
  12.      *  消息日期 
  13.      */ 
  14.     protected String date ; 

消息對象子類

1、Ping檢查消息

  1. public class PingMessage extends AbstractMessage { 
  2.     public PingMessage() {} 
  3.     public PingMessage(String type) { 
  4.         this.type = type ; 
  5.     } 

 2、系統(tǒng)消息

  1. public class SystemMessage extends AbstractMessage { 
  2.     public SystemMessage() {} 
  3.     public SystemMessage(String type, String content) { 
  4.         this.type = type ; 
  5.         this.content = content ; 
  6.     } 

 3、點對點消息

  1. public class PersonMessage extends AbstractMessage { 
  2.     private String fromName ; 
  3.     private String toName ; 

 消息類型定義 

  1. public enum MessageType { 
  2.      
  3.     /** 
  4.      *  系統(tǒng)消息 0000;心跳檢查消息 0001;點對點消息2001 
  5.      */ 
  6.     SYSTEM("0000"), PING("0001"), PERSON("2001") ; 
  7.      
  8.     private String type ; 
  9.      
  10.     private MessageType(String type) { 
  11.         this.type = type ; 
  12.     } 
  13.  
  14.     public String getType() { 
  15.         return type; 
  16.     } 
  17.  
  18.     public void setType(String type) { 
  19.         this.type = type; 
  20.     } 
  21.      

 WebSocket服務(wù)端點

該類作用就是定義客戶端連接的地址

  1. @ServerEndpoint(value = "/message/{username}",  
  2.     encoders = {WsMessageEncoder.class}, 
  3.     decoders = {WsMessageDecoder.class}, 
  4.     subprotocols = {"gmsg"}, 
  5.     configurator = MessageConfigurator.class)   
  6. @Component   
  7. public class GMessageListener {   
  8.    
  9.     public static ConcurrentMap<String, UserSession> sessions = new ConcurrentHashMap<>(); 
  10.     private static Logger logger = LoggerFactory.getLogger(GMessageListener.class) ; 
  11.    
  12.     private String username ; 
  13.      
  14.     @OnOpen   
  15.     public void onOpen(Session session, EndpointConfig config, @PathParam("username") String username){ 
  16.         UserSession userSession = new UserSession(session.getId(), username, session) ; 
  17.         this.username = username ; 
  18.         sessions.put(username, userSession) ; 
  19.         logger.info("【{}】用戶進(jìn)入, 當(dāng)前連接數(shù):{}", username, sessions.size()) ;  
  20.     }   
  21.    
  22.     @OnClose   
  23.     public void onClose(Session session, CloseReason reason){   
  24.         UserSession userSession = sessions.remove(this.username) ; 
  25.         if (userSession != null) { 
  26.             logger.info("用戶【{}】, 斷開連接, 當(dāng)前連接數(shù):{}", username, sessions.size()) ; 
  27.         } 
  28.     } 
  29.      
  30.     @OnMessage 
  31.     public void pongMessage(Session session, PongMessage message) { 
  32.         ByteBuffer buffer = message.getApplicationData() ; 
  33.         logger.debug("接受到Pong幀【這是由瀏覽器發(fā)送】:" + buffer.toString()); 
  34.     } 
  35.      
  36.     @OnMessage 
  37.     public void onMessage(Session session, AbstractMessage message) { 
  38.         if (message instanceof PingMessage) { 
  39.             logger.debug("這里是ping消息"); 
  40.             return ; 
  41.         } 
  42.         if (message instanceof PersonMessage) { 
  43.             PersonMessage personMessage = (PersonMessage) message ; 
  44.             if (this.username.equals(personMessage.getToName())) { 
  45.                 logger.info("【{}】收到消息:{}", this.username, personMessage.getContent()); 
  46.             } else { 
  47.                 UserSession userSession = sessions.get(personMessage.getToName()) ; 
  48.                 if (userSession != null) { 
  49.                     try { 
  50.                         userSession.getSession().getAsyncRemote().sendText(new ObjectMapper().writeValueAsString(message)) ; 
  51.                     } catch (JsonProcessingException e) { 
  52.                         e.printStackTrace(); 
  53.                     } 
  54.                 } 
  55.             } 
  56.             return ; 
  57.         } 
  58.         if (message instanceof SystemMessage) { 
  59.             logger.info("接受到消息類型為【系統(tǒng)消息】") ;  
  60.             return ; 
  61.         } 
  62.     } 
  63.      
  64.     @OnError 
  65.     public void onError(Session session, Throwable error) { 
  66.         logger.error(error.getMessage()) ; 
  67.     } 

 WsMessageEncoder.java類

該類的主要作用是,當(dāng)發(fā)送的消息是對象時,該如何轉(zhuǎn)換

  1. public class WsMessageEncoder implements Encoder.Text<AbstractMessage> { 
  2.     private static Logger logger = LoggerFactory.getLogger(WsMessageDecoder.class) ; 
  3.     @Override 
  4.     public void init(EndpointConfig endpointConfig) { 
  5.     } 
  6.     @Override 
  7.     public void destroy() { 
  8.     } 
  9.     @Override 
  10.     public String encode(AbstractMessage tm) throws EncodeException { 
  11.         String message = null ; 
  12.         try { 
  13.             message = new ObjectMapper().writeValueAsString(tm); 
  14.         } catch (JsonProcessingException e) { 
  15.             logger.error("JSON處理錯誤:{}", e) ; 
  16.         } 
  17.         return message; 
  18.     } 

 WsMessageDecoder.java類

該類的作用是,當(dāng)接收到消息時如何轉(zhuǎn)換成對象。

  1. public class WsMessageDecoder implements  Decoder.Text<AbstractMessage> { 
  2.  
  3.     private static Logger logger = LoggerFactory.getLogger(WsMessageDecoder.class) ; 
  4.     private static Set<String> msgTypes = new HashSet<>() ; 
  5.      
  6.     static { 
  7.         msgTypes.add(MessageType.PING.getType()) ; 
  8.         msgTypes.add(MessageType.SYSTEM.getType()) ; 
  9.         msgTypes.add(MessageType.PERSON.getType()) ; 
  10.     } 
  11.     @Override 
  12.     @SuppressWarnings("unchecked"
  13.     public AbstractMessage decode(String s) throws DecodeException { 
  14.         AbstractMessage message = null ; 
  15.         try { 
  16.             ObjectMapper mapper = new ObjectMapper() ; 
  17.             Map<String,String> map = mapper.readValue(s, Map.class) ; 
  18.             String type = map.get("type") ; 
  19.             switch(type) { 
  20.                 case "0000"
  21.                     message = mapper.readValue(s, SystemMessage.class) ; 
  22.                     break; 
  23.                 case "0001"
  24.                     message = mapper.readValue(s, PingMessage.class) ; 
  25.                     break; 
  26.                 case "2001"
  27.                     message = mapper.readValue(s, PersonMessage.class) ; 
  28.                     break; 
  29.             } 
  30.         } catch (JsonProcessingException e) { 
  31.             logger.error("JSON處理錯誤:{}", e) ; 
  32.         } 
  33.         return message ; 
  34.     } 
  35.  
  36.     // 該方法判斷消息是否可以被解碼(轉(zhuǎn)換) 
  37.     @Override 
  38.     @SuppressWarnings("unchecked"
  39.     public boolean willDecode(String s) { 
  40.         Map<String, String> map = new HashMap<>() ; 
  41.         try { 
  42.             map = new ObjectMapper().readValue(s, Map.class); 
  43.         } catch (JsonProcessingException e) { 
  44.             e.printStackTrace(); 
  45.         } 
  46.         logger.debug("檢查消息:【" + s + "】是否可以解碼") ; 
  47.         String type = map.get("type") ; 
  48.         if (StringUtils.isEmpty(type) || !msgTypes.contains(type)) { 
  49.             return false ; 
  50.         } 
  51.         return true ; 
  52.     } 
  53.     @Override 
  54.     public void init(EndpointConfig endpointConfig) { 
  55.     } 
  56.     @Override 
  57.     public void destroy() { 
  58.     } 

 MessageConfigurator.java類

該類的作用是配置服務(wù)端點,比如配置握手信息

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

 WebSocke配置類

  1. @Configuration 
  2. public class WebSocketConfig { 
  3.      
  4.     @Bean 
  5.     public ServerEndpointExporter serverEndpointExporter (){   
  6.         return new ServerEndpointExporter();   
  7.     }   
  8.      

 當(dāng)以jar包形式運行時需要配置該bean,暴露我們配置的@ServerEndpoint;當(dāng)我們以war獨立tomcat運行時不能配置該bean。

前端頁面

  1. <!doctype html> 
  2. <html> 
  3.  <head> 
  4.   <meta charset="UTF-8"
  5.   <meta name="Author" content=""
  6.   <meta name="Keywords" content=""
  7.   <meta name="Description" content=""
  8.   <script src="g-messages.js?v=1"></script> 
  9.   <title>WebSocket</title> 
  10.   <style type="text/css"
  11.   </style> 
  12.   <script> 
  13.     let gm = null ; 
  14.     let username = null ; 
  15.     function ListenerMsg({url, protocols = ['gmsg'], options = {}}) { 
  16.         if (!url){  
  17.             throw new Error("未知服務(wù)地址") ; 
  18.         } 
  19.         gm = new window.__GM({ 
  20.             url: url, 
  21.             protocols: protocols 
  22.         }) ; 
  23.         gm.open(options) ; 
  24.     } 
  25.     ListenerMsg.init = (user) => { 
  26.         if (!user) { 
  27.             alert("未知的當(dāng)前登錄人") ; 
  28.             return ; 
  29.         } 
  30.         let url = `ws://localhost:8080/message/${user}` ; 
  31.         let msg = document.querySelector("#msg"
  32.         ListenerMsg({url, options: { 
  33.             onmessage (e) { 
  34.                 let data = JSON.parse(e.data) ; 
  35.                 let li = document.createElement("li") ; 
  36.                 li.innerHTML = "【" + data.fromName + "】對你說:" + data.content ; 
  37.                 msg.appendChild(li) ; 
  38.             } 
  39.         }}) ; 
  40.     } 
  41.     function enter() { 
  42.         username = document.querySelector("#nick").value ; 
  43.         ListenerMsg.init(username) ; 
  44.         document.querySelector("#chat").style.display = "block" ; 
  45.         document.querySelector("#enter").style.display = "none" ; 
  46.         document.querySelector("#cu").innerText = username ; 
  47.     } 
  48.     function send() { 
  49.         let a = document.querySelector("#toname") ; 
  50.         let b = document.querySelector("#content") ; 
  51.         let toName = a.value ; 
  52.         let content = b.value ; 
  53.         gm.sendMessage({type: "2001", content, fromName: username, toName}) ; 
  54.         a.value = '' ; 
  55.         b.value = '' ; 
  56.     } 
  57.   </script> 
  58.  </head> 
  59.  <body> 
  60.     <div id="enter"
  61.         <input id="nick"/><button type="button" onclick="enter()">進(jìn)入</button> 
  62.     </div> 
  63.     <hr/> 
  64.     <div id="chat" style="display:none;"
  65.         當(dāng)前用戶:<b id="cu"></b><br/> 
  66.         用戶:<input id="toname" name="toname"/><br/><br/> 
  67.         內(nèi)容:<textarea id="content" rows="3" cols="22"></textarea><br/> 
  68.         <button type="button" onclick="send()">發(fā)送</button> 
  69.     </div> 
  70.     <div> 
  71.         <ul id="msg"
  72.         </ul> 
  73.     </div> 
  74.  </body> 
  75. </html> 

到此所有的代碼完畢,接下來測試

測試

打開兩個標(biāo)簽頁,以不同的用戶進(jìn)入。

輸入對方用戶名發(fā)送消息

成功了,簡單的websocket。我們生產(chǎn)環(huán)境還就這么完的,8g內(nèi)存跑了6w的用戶。

完畢!!!

 

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2023-08-14 08:01:12

websocket8g用戶

2020-10-09 12:45:19

創(chuàng)建消息即時消息編程語言

2020-10-09 15:00:56

實時消息編程語言

2019-09-29 15:25:13

CockroachDBGoJavaScript

2019-10-28 20:12:40

OAuthGuard中間件編程語言

2020-03-31 12:21:20

JSON即時消息編程語言

2020-10-12 09:20:13

即時消息Access頁面編程語言

2020-10-19 16:20:38

即時消息Conversatio編程語言

2020-10-16 14:40:20

即時消息Home頁面編程語言

2021-02-05 07:28:11

SpringbootNettyWebsocke

2015-03-18 15:37:19

社交APP場景

2020-10-10 20:51:10

即時消息編程語言

2022-06-28 08:37:07

分布式服務(wù)器WebSocket

2023-07-26 07:28:55

WebSocket服務(wù)器方案

2021-03-26 08:16:32

SpringbootWebsocket前端

2024-09-02 09:31:19

2010-05-24 09:51:37

System Cent

2024-09-12 14:50:08

2024-11-14 11:56:45

2024-09-11 08:35:54

點贊
收藏

51CTO技術(shù)棧公眾號