博客
关于我
Springboot2模块系列:websocket(即时消息推送)
阅读量:253 次
发布时间:2019-03-01

本文共 7822 字,大约阅读时间需要 26 分钟。

1 配置

1.0 pom.xml

org.springframework.boot
spring-boot-starter-websocket
org.springframework.boot
spring-boot-starter-thymeleaf

1.2 静态文件加载

package com.company.system.config;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;import org.springframework.context.annotation.Configuration;@Configurationpublic class StaticResourceLoadingConfig implements WebMvcConfigurer{       @Override     public void addResourceHandlers(ResourceHandlerRegistry registry){           registry.addResourceHandler("/**")        .addResourceLocations("classpath:/resources/")        .addResourceLocations("classpath:/static/")        .addResourceLocations("classpath:/css");    } }

1.2 websocket配置

package com.company.system.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/** * 开启WebSocket支持 */@Configuration  public class WebSocketConfig {        @Bean      public ServerEndpointExporter serverEndpointExporter() {             return new ServerEndpointExporter();    }  }

2 Websocket路由

package com.company.system.config;import java.io.IOException;import java.util.concurrent.ConcurrentHashMap;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import org.apache.commons.lang.StringUtils;import org.springframework.stereotype.Component;import org.slf4j.Logger;import org.slf4j.LoggerFactory;@ServerEndpoint("/imserver/{userId}")@Componentpublic class WebSocketServerConfig {       static Logger log=LoggerFactory.getLogger(WebSocketServerConfig.class);    //当前在线连接数    private static int onlineCount = 0;    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象    private static ConcurrentHashMap
webSocketMap = new ConcurrentHashMap<>(); //与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; //接收userId private String userId=""; /** * 连接建立成功调用的方法*/ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) { this.session = session; this.userId=userId; if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); webSocketMap.put(userId,this); //加入set中 }else{ webSocketMap.put(userId,this); //加入set中 addOnlineCount(); //在线数加1 } log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); try { sendMessage("我在线上"); } catch (IOException e) { log.error("用户:"+userId+",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { if(webSocketMap.containsKey(userId)){ webSocketMap.remove(userId); //从set中删除 subOnlineCount(); } log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * @param message 发送的信息 * @param session 会话 */ @OnMessage public void onMessage(String message, Session session) { log.info("用户消息:"+userId+",报文:"+message); //可以群发消息 //消息保存到数据库、redis if(StringUtils.isNotBlank(message)){ try { //解析发送的报文 JSONObject jsonObject = JSON.parseObject(message); //追加发送人(防止串改) jsonObject.put("fromUserId",this.userId); String toUserId=jsonObject.getString("toUserId"); //传送给对应toUserId用户的websocket if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){ webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString()); }else{ log.error("请求的userId:"+toUserId+"不在该服务器上"); //否则不在这个服务器上,发送到mysql或者redis } }catch (Exception e){ e.printStackTrace(); } } } /** * 数据传输错误 * @param session 会话 * @param error 错误 */ @OnError public void onError(Session session, Throwable error) { log.error("用户错误:"+this.userId+",原因:"+error.getMessage()); error.printStackTrace(); } /** * 服务端向客户端推送消息 * @param message 推送的消息 * @throws IOException 抛出异常 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 自定义发送消息 * @param message 发送的消息 * @param userId 用户ID * @throws IOException 抛出异常 */ public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException { log.info("发送消息到:"+userId+",报文:"+message); if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){ webSocketMap.get(userId).sendMessage(message); }else{ log.error("用户"+userId+",不在线!"); } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServerConfig.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServerConfig.onlineCount--; }}

3 自定义回调接口

package com.company.system.controller;import java.util.Map;import java.util.HashMap;import com.company.system.config.WebSocketServerConfig;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.CrossOrigin;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;import org.springframework.stereotype.Controller;import org.springframework.web.servlet.ModelAndView;import java.io.IOException;@CrossOrigin(origins="*", maxAge=3600)@RequestMapping("/websocket")@Controllerpublic class MsgController {       /**     * web消息发送测试     * @return     */    @GetMapping("/instance/message")    public String page(){           return "websocket/chatpage";    }    /**     * 回调接口,向客户端推送实时消息     * @param params 消息数据结构     * @param toUserId 发动到用户ID     * @return     * @throws IOException 异常     */    @RequestMapping(value="/push/{toUserId}", method=RequestMethod.POST)    @ResponseBody    public Map pushToWeb(@RequestBody Map params,@PathVariable String toUserId) throws IOException {           String message = params.get("message").toString();        WebSocketServerConfig.sendInfo(message,toUserId);        Map returnMap = new HashMap();        returnMap.put("code", 200);        returnMap.put("msg", "成功发送消息");        return returnMap;    }}

4 对话消息展示

4.1 前端html

    
websocket即时消息

当前用户

好友

消息记录

操作

消息记录

4.2 对话展示

浏览器打开两个页面,分别访问:http://localhost:10106/websocket/instance/message

在这里插入图片描述

图4.1 用户A

在这里插入图片描述

图4.2 用户B

4.3 接口推送消息

通过接口给小红发送消息,在用户小红对话框历史中可以看到该条记录:测试消息.

若使用接口包含中文,提示Error: Request path contains unescaped characters,
将URL中的中文编码:
在这里插入图片描述

图4.3 修改数据编码

编码后的URL:http://localhost:10106/websocket/push/%E5%B0%8F%E8%8A%B1

在这里插入图片描述

图4.4 接口推送消息

在这里插入图片描述

图4.5 小红的历史消息记录

【参考文献】

[1]
[2]
[3]
[4]
[5]
[6]
[7]

转载地址:http://npht.baihongyu.com/

你可能感兴趣的文章
mapping文件目录生成修改
查看>>
MapReduce程序依赖的jar包
查看>>
mariadb multi-source replication(mariadb多主复制)
查看>>
MariaDB的简单使用
查看>>
MaterialForm对tab页进行隐藏
查看>>
Member var and Static var.
查看>>
memcached高速缓存学习笔记001---memcached介绍和安装以及基本使用
查看>>
memcached高速缓存学习笔记003---利用JAVA程序操作memcached crud操作
查看>>
Memcached:Node.js 高性能缓存解决方案
查看>>
memcache、redis原理对比
查看>>
memset初始化高维数组为-1/0
查看>>
Metasploit CGI网关接口渗透测试实战
查看>>
Metasploit Web服务器渗透测试实战
查看>>
MFC模态对话框和非模态对话框
查看>>
Moment.js常见用法总结
查看>>
MongoDB出现Error parsing command line: unrecognised option ‘--fork‘ 的解决方法
查看>>
mxGraph改变图形大小重置overlay位置
查看>>
MongoDB可视化客户端管理工具之NoSQLbooster4mongo
查看>>
Mongodb学习总结(1)——常用NoSql数据库比较
查看>>
MongoDB学习笔记(8)--索引及优化索引
查看>>