博客
关于我
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/

你可能感兴趣的文章
mysql CPU使用率过高的一次处理经历
查看>>
Multisim中555定时器使用技巧
查看>>
MySQL CRUD 数据表基础操作实战
查看>>
multisim变压器反馈式_穿过隔离栅供电:认识隔离式直流/ 直流偏置电源
查看>>
mysql csv import meets charset
查看>>
multivariate_normal TypeError: ufunc ‘add‘ output (typecode ‘O‘) could not be coerced to provided……
查看>>
MySQL DBA 数据库优化策略
查看>>
multi_index_container
查看>>
MySQL DBA 进阶知识详解
查看>>
Mura CMS processAsyncObject SQL注入漏洞复现(CVE-2024-32640)
查看>>
Mysql DBA 高级运维学习之路-DQL语句之select知识讲解
查看>>
mysql deadlock found when trying to get lock暴力解决
查看>>
MuseTalk如何生成高质量视频(使用技巧)
查看>>
mutiplemap 总结
查看>>
MySQL DELETE 表别名问题
查看>>
MySQL Error Handling in Stored Procedures---转载
查看>>
MVC 区域功能
查看>>
MySQL FEDERATED 提示
查看>>
mysql generic安装_MySQL 5.6 Generic Binary安装与配置_MySQL
查看>>
Mysql group by
查看>>