spring boot 搭建websocket_WebSocket 整合 Springboot

WebSocket+Springboot

1.1 pom 文件的依赖和插件

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.9.RELEASE</version></parent><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--servlet3.1规范--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version></dependency><dependency><groupId>org.glassfish.web</groupId><artifactId>jsp</artifactId><version>2.2</version></dependency><!-- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api websocket 依赖 --><dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version><scope>provided</scope></dependency><!--用于处理json 数据的--><dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket spring 整合websocket --><dependency><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId></dependency><!--springboot 需要的依赖包--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency></dependencies><build><finalName>websocketspring</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

1.2 WebSocket 的配置文件

用于启动 websocket,注入处理器和拦截器

/*** Created by jackiechan on 2018/2/5/下午4:05*/@Configuration //声明为配置文件@EnableWebSocket//启用 websocketpublic class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {System.out.println("初始化路径拦截");//指定所有/websocket开头的路径会被 websocket 拦截,设置处理器和拦截器 webSocketHandlerRegistry.addHandler(chatMessageHandler(),"/websocket/*").addInterceptors(new ChatHandshakeInterceptor()); }/*** 创建处理器* @return*/@Beanpublic TextWebSocketHandler chatMessageHandler(){System.out.println("创建 handler");return new ChatMessageHandler(); }}

1.3 ChatHandshakeInterceptor拦截器

用于每次 websocket 在握手之前进行拦截,可以在内部进行校验

/*** Created by jackiechan on 2018/2/5/下午4:16** WebSocket握手请求的拦截器. 检查握手请求和响应, 对WebSocketHandler传递属性*/public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {/*** 在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性* @param request* @param response* @param wsHandler* @param attributes* @return* @throws Exception*/@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,Map<String, Object> attributes) throws Exception {//为了方便区分来源,在此以用户的名字来区分,名字我们通过要求用输入进行传递,所以在这里先从请求中获取到用户输入的名字,因为是使用的rest 风格,所以规定路径的最后一个字符串是名字System.out.println("握手之前");String s = request.getURI().toString();String s1 = s.substring(s.lastIndexOf("/") + 1); attributes.put(Constants.WEBSOCKET_USERNAME, s1);//给当前连接设置属性return super.beforeHandshake(request, response, wsHandler, attributes); }/*** 在握手之后执行该方法. 无论是否握手成功都指明了响应状态码和相应头.* @param request* @param response* @param wsHandler* @param ex*/@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,Exception ex) {System.out.println("After Handshake");super.afterHandshake(request, response, wsHandler, ex); }}

1.4 ChatMessageHandler消息处理器

用于收到消息的时候处理消息

/*** Created by jackiechan on 2018/2/5/下午4:11* 文本消息的处理器*/public class ChatMessageHandler extends TextWebSocketHandler {private static final Map<String,WebSocketSession> allClients;//用于缓存所有的用户和连接之间的关系private static Logger logger = Logger.getLogger(ChatMessageHandler.class);static { allClients = new ConcurrentHashMap();//初始化连接 }/*** 当和用户成功建立连接的时候会调用此方法,在此方法内部应该保存连接*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {System.out.println("建立连接成功");String name = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);//将在拦截器中保存的用户的名字取出来,然后作为 key 存到 map 中if (name != null) { allClients.put(name, session);//保存当前的连接和用户之间的关系 }// 这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户 }/*** 收到消息的时候会触发该方法* @param session 发送消息的用户的 session* @param message 发送的内容* @throws Exception*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {//此处请根据自己的具体业务逻辑做处理 JSONObject jsonObject= JSONObject.fromObject(new String(message.asBytes()));//将用户发送的消息转换为 json,实际开发中请根据自己的需求处理String toName = jsonObject.getString("toName");//获取数据中的收消息人的名字String content = jsonObject.getString("content");//获取到发送的内容String fromName = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);//获取当前发送消息的人的名字 content = "收到来自:" +fromName+ "的消息,内容是:" + content;//拼接内容转发给接收者,实际开发中请参考自己的需求做处理 TextMessage textMessage = new TextMessage(content);//将内容转换为 TextMessagesendMessageToUser(toName,textMessage);// 发送给指定的用户//sendMessageToUsers(message);//给所有人发送//super.handleTextMessage(session, message); }/*** 给某个用户发送消息** @param userName* @param message*/public void sendMessageToUser(String userName, TextMessage message) { WebSocketSession webSocketSession = allClients.get(userName);//根据接收方的名字找到对应的连接if (webSocketSession != null&& webSocketSession.isOpen()) {//如果没有离线,如果离线,请根据实际业务需求来处理,可能会需要保存离线消息try { webSocketSession.sendMessage(message);//发送消息 } catch (IOException e) { e.printStackTrace(); } } }/*** 给所有在线用户发送消息,此处以文本消息为例子** @param message*/public void sendMessageToUsers(TextMessage message) {for (Map.Entry<String, WebSocketSession> webSocketSessionEntry : allClients.entrySet()) {//获取所有的连接 WebSocketSession session = webSocketSessionEntry.getValue();//找到每个连接if (session != null&& session.isOpen()) {try { session.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } }/*** 出现异常的时候* @param session* @param exception* @throws Exception*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {String name = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);if (session.isOpen()) { session.close(); } logger.debug("连接关闭"); allClients.remove(name);//移除连接 }/*** 连接关闭后* @param session* @param closeStatus* @throws Exception*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { logger.debug("连接关闭");String name = (String) session.getAttributes().get(Constants.WEBSOCKET_USERNAME);//找到用户对应的连接 allClients.remove(name);//移除 }@Overridepublic boolean supportsPartialMessages() {return false; }}

1.5 springboot 启动类

注意此类最好放在根包下

/*** Created by jackiechan on 2018/2/5/下午4:34*/@SpringBootApplication@Configurationpublic class App {public static void main(String[] args) { SpringApplication.run(App.class, args);// spingboot } }}

1.6 web 方式启动项的配置类

/*** Created by jackiechan on 2018/2/5/下午4:34用于将项目打包成 war 包后发布*/public class SpringBootStartApplication extends SpringBootServletInitializer {@Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {return builder.sources(App.class); }}

1.7 html

与非 springboot 的方式内容一致

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title><script type="text/javascript">var websocket = null;function abc() {//var username = localStorage.getItem("name");var username=document.getElementById("me").value;//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) { websocket = new WebSocket("ws://" + document.location.host + "/websocket/"+username);} else {alert('当前浏览器 Not support websocket')}//连接发生错误的回调方法websocket.onerror = function() {setMessageInnerHTML("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function() {setMessageInnerHTML("WebSocket连接成功");}//接收到消息的回调方法websocket.onmessage = function(event) {setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function() {setMessageInnerHTML("WebSocket连接关闭");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function() {closeWebSocket();}}/** * 发送消息 */function sendmessage() {var toName=document.getElementById("to").value;if (websocket!=null) {var content=document.getElementById("content").value;var message='{"toName":"'+toName+'","content":"'+content+'"}';//将发送的内容拼接为 json 字符串,服务端用于解析好处理websocket.send(message);}}//关闭WebSocket连接function closeWebSocket() {if (websocket!=null) {websocket.close();}}function setMessageInnerHTML(data) {document.getElementById("neirong").innerHTML = data;}</script></head><body>用户名:<input type="text" id="me" /> <button οnclick="abc()"> 连接</button><br><!--实际接收者应该由用户选择,或者由系统安排,比如客服的话,应该是服务端已经存储了所有在线的客服,用户只需要发送消息即可,如果是两个用户聊天,则应该有用户列表,选择后指定目标-->接收者:<input type="text" id="to" /><br>内容:<input type="text" id="content" /><br><button οnclick="sendmessage()">发送</button><br><br><br><br><span id="neirong"></span></body></html>

1.8 启动测试

v2-44719728b9e8d3dd14a7dc5cb5074cf5_b.jpg

v2-3c40829a650f96ab57be6975d78f8601_b.jpg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值