今天就跟大家聊聊有关SpringBoot中怎么利用WebSocket实现即时消息,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
<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 ; }
消息对象子类
1、Ping检查消息
public class PingMessage extends AbstractMessage { public PingMessage() {} public PingMessage(String type) { this.type = type ; } }
2、系统消息
public class SystemMessage extends AbstractMessage { public SystemMessage() {} public SystemMessage(String type, String content) { this.type = type ; this.content = content ; } }
3、点对点消息
public class PersonMessage extends AbstractMessage { private String fromName ; private String toName ; }
消息类型定义
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; } }
该类作用就是定义客户端连接的地址
@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类
该类的主要作用是,当发送的消息是对象时,该如何转换
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类
该类的作用是,当接收到消息时如何转换成对象。
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类
该类的作用是配置服务端点,比如配置握手信息
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配置类
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter (){ return new ServerEndpointExporter(); } }
当以jar包形式运行时需要配置该bean,暴露我们配置的@ServerEndpoint;当我们以war独立tomcat运行时不能配置该bean。
<!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> <div id="enter"> <input id="nick"/><button type="button" onclick="enter()">进入</button> </div> <hr/> <div 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>
到此所有的代码完毕,接下来测试
打开两个标签页,以不同的用户进入。
输入对方用户名发送消息
看完上述内容,你们对SpringBoot中怎么利用WebSocket实现即时消息有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。