Springboot集成WebSocket实现消息推送功能

一、导入依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>

<artifactId>demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>demo</name>

<description>Demo project for Spring Boot</description>

<properties>

<java.version>1.8</java.version>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<spring-boot.version>2.3.7.RELEASE</spring-boot.version>

</properties>

<dependencies>

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

        <groupId>com.alibaba</groupId>

        <artifactId>fastjson</artifactId>

        <version>1.2.72</version>

</dependency>

<dependency>

        <groupId>org.projectlombok</groupId>

        <artifactId>lombok</artifactId>

        <optional>true</optional>

</dependency>

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>org.junit.vintage</groupId>

<artifactId>junit-vintage-engine</artifactId>

</exclusion>

</exclusions>

</dependency>

<!--websocket-->

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

<dependency>

        <groupId>junit</groupId>

        <artifactId>junit</artifactId>

</dependency>

<!--工具包-->

<dependency>

        <groupId>org.apache.commons</groupId>

        <artifactId>commons-lang3</artifactId>

        <version>3.5</version>

</dependency>

</dependencies>

        <dependencyManagement>

<dependencies>

<dependency>

        <groupId>org.springframework.boot</groupId>

         <artifactId>spring-boot-dependencies</artifactId>

        <version>${spring-boot.version}</version>

        <type>pom</type>

        <scope>import</scope>

</dependency>

</dependencies>

</dependencyManagement>

<build>

        <plugins>

                <plugin>

                        <groupId>org.apache.maven.plugins</groupId>

                        <artifactId>maven-compiler-plugin</artifactId>

                        <version>3.8.1</version>

                        <configuration>

                        <source>1.8</source>

                        <target>1.8</target>

                        <encoding>UTF-8</encoding>

                </configuration>

                </plugin>

                <plugin>

                        <groupId>org.springframework.boot</groupId>

                        <artifactId>spring-boot-maven-plugin</artifactId>

                        <version>2.3.7.RELEASE</version>

        <configuration>

                <mainClass>com.example.demo.DemoApplication</mainClass>

        </configuration>

        <executions>

                <execution>

                        <id>repackage</id>

                        <goals>

                        <goal>repackage</goal>

                        </goals>

                </execution>

        </executions>

</plugin> </plugins>

</build>

</project>

二、application.yml配置文件

server: port: 8080

spring:

        http: encoding:UTF-8

        freemarker:

                request-context-attribute: request

                #prefix: /templates/

                suffix: .html

                content-type: text/html

                enabled: true

                cache: false

                charset: UTF-8

                allow-request-override: false

                expose-request-attributes: true

                expose-session-attributes: true

                 expose-spring-macro-helpers: true

三、WebSocketConfig: 用于开启WebSocket支持

package com.example.demo.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/** * @author lyd * @Description: 开启WebSocket支持 * @date 15:43 */

@Configuration

public class WebSocketConfig {

        @Bean

        public ServerEndpointExporter serverEndpointExporter(){

                return new ServerEndpointExporter();

        }

}

四、WebSocketServer: 核心类,用于开启、关闭连接,以及接收消息等

比较重要的注解 @ServerEndpoint("") 、@OnOpen 、@onClose@onMessage

package com.example.demo.server;

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.springframework.stereotype.Component;

import javax.websocket.*;

import javax.websocket.server.PathParam;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;

import java.util.concurrent.ConcurrentHashMap;

/** * @author lyd *

@Description: 因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller, * 所以可以直接在前端调用@ServerEndpoint("")中的路径,相当于掉接口了 *

@date 15:48 */

@Slf4j

@Component

@ServerEndpoint("/imserver/{userId}")

public class WebSocketServer {

/** * 静态变量,用来记录当前在线连接数 */

private static int onlineCount = 0;

/** * Concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象 */

private static ConcurrentHashMap<String, WebSocketServer> 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);

                addOnlineCount();

        } else {

                webSocketMap.put(userId, this);

                addOnlineCount();

        }

        log.info("用户" + userId + "连接,当前在线人数为:" + getOnlineCount());

        try {

                sendMessage("连接成功"); 

        } catch (IOException e) {

                log.error("用户:" + userId + ",网络异常,哈哈哈"); e.printStackTrace();

        }

}

/** * 连接关闭调用的方法 */

@OnClose

public void onClose() {

        if (webSocketMap.containsKey(userId)) {

                webSocketMap.remove(userId); subOnlineCount();

        }

        log.info("用户:" + userId + "退出成功,当前在线人数为:" + getOnlineCount());

}

/** * 收到客户端消息后调用的方法 */

@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");

                        if (StringUtils.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)) {                                 webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());                         }else {

                                log.error("请求的userId:"+toUserId+"不在该服务器上");

                        }

                 } catch (IOException e) {

                        e.printStackTrace();

                }

        }

}

/** * 实现服务器消息主动推送 * * @param message * @throws IOException */

public void sendMessage(String message) throws IOException {         this.session.getBasicRemote().sendText(message);

}

/** * 发送自定义消息 */

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 + ",不在线!");

        }

}

/** * 获得当前连接数 * * @return */

public static synchronized int getOnlineCount() {

        return onlineCount;

}

/** * 在线连接数加1 */

public static synchronized void addOnlineCount() {

         WebSocketServer.onlineCount++;

}

/** * 在线连接数减1 */

public static synchronized void subOnlineCount() {

WebSocketServer.onlineCount--;

}

/** * @param session * @param error */

@OnError

public void onError(Session session, Throwable error) {

        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());

         error.printStackTrace();

}

}

五、前端页面代码

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>websocket通讯</title>

</head>

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>

<script>

        var socket;

        function openSocket() {

                if (typeof (WebSocket) == "undefined") {

                        console.log("您的浏览器不支持WebSocket");

                } else {

                        console.log("您的浏览器支持WebSocket");

                        //实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接

                        //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");

                        //var socketUrl="${request.contextPath}/im/"+$("#userId").val();

                        var socketUrl = "http://localhost:8080/imserver/" + $("#userId").val();

                        socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");

                        console.log(socketUrl);

                        if (socket != null) {

                                socket.close(); socket = null;

                        }

                        socket = new WebSocket(socketUrl);

                        //打开事件

                        socket.onopen = function () {

                                console.log("websocket已打开");

                                //socket.send("这是来自客户端的消息" + location.href + new Date());

                        };

                        //获得消息事件

                        socket.onmessage = function (msg) {

                               console.log(msg.data); //发现消息进入 开始处理前端触发逻辑

                        };

                        //关闭事件

                        socket.onclose = function () {

                                console.log("websocket已关闭");

                        };

                        //发生了错误事件

                        socket.onerror = function () {

                                console.log("websocket发生了错误");

                        }

                }

}

function sendMessage() {

        if (typeof (WebSocket) == "undefined") {

                console.log("您的浏览器不支持WebSocket");

        } else {

                console.log("您的浏览器支持WebSocket");

                console.log('{"toUserId":"' + $("#toUserId").val() + '","contentText":"' + $("#contentText").val() + '"}');

                socket.send('{"toUserId":"' + $("#toUserId").val() + '","contentText":"' + $("#contentText").val() + '"}');

        }

}

</script>

<body>

<p>【userId】: <div><input id="userId" name="userId" type="text" value="10"></div>

<p>【toUserId】: <div><input id="toUserId" name="toUserId" type="text" value="20"></div> <p>【发送内容】: <div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>

<p>【操作】: <div><a οnclick="openSocket()">开启socket</a></div>

<p>【操作】: <div><a οnclick="sendMessage()">发送消息</a></div>

</body>

</html>

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Spring Boot集成WebSocket实现消息推送,需要进行以下步骤: 1. 添加Spring Boot WebSocket依赖 2. 创建WebSocket配置类 3. 创建WebSocket处理器类 4. 创建WebSocket拦截器类 5. 创建WebSocket消息模型类 6. 在Spring Boot中使用WebSocket 下面是一个简单的示例代码: 1. 添加Spring Boot WebSocket依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建WebSocket配置类 创建一个WebSocket配置类,用于配置WebSocket相关的参数,如下所示: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/websocket").addInterceptors(new WebSocketInterceptor()); } } ``` 3. 创建WebSocket处理器类 创建一个WebSocket处理器类,用于处理WebSocket连接、断开连接和接收消息等操作,如下所示: ``` public class WebSocketHandler extends TextWebSocketHandler { private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(message); } } } ``` 4. 创建WebSocket拦截器类 创建一个WebSocket拦截器类,用于拦截WebSocket连接请求,如下所示: ``` public class WebSocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } } ``` 5. 创建WebSocket消息模型类 创建一个WebSocket消息模型类,用于封装WebSocket消息,如下所示: ``` public class WebSocketMessage { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ``` 6. 在Spring Boot中使用WebSocketSpring Boot中使用WebSocket非常简单,只需要在Controller中注入WebSocketSession即可,如下所示: ``` @Controller public class WebSocketController { @Autowired private WebSocketSession session; @MessageMapping("/send") public void send(WebSocketMessage message) throws Exception { session.sendMessage(new TextMessage(message.getContent())); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值