java通信:Springboot整合Websocket

Springboot整合Websocket

什么是webSocket?

WebSocket(Web套接字)是一种在单个 TCP 连接上实现全双工通信的协议,允许客户端和服务器之间进行双向实时通信。WebSocket 是 HTML5 标准的一部分,其主要特点包括:

  1. 双向通信: WebSocket 允许服务器向客户端主动推送消息,同时也允许客户端发送消息给服务器,实现了双向通信。
  2. 持久连接: 与传统的 HTTP 请求-响应模型不同,WebSocket 连接在建立后可以一直保持,而不需要为每个消息都建立新的连接,减少了通信的延迟和开销。
  3. 低延迟: WebSocket 具有较低的通信延迟,适用于需要实时性的应用,如在线聊天、实时数据监控、在线游戏等。
  4. 跨域支持: WebSocket 支持跨域通信,可以在不同域名的服务器之间建立连接。
  5. 轻量级头部: WebSocket 协议的头部数据较小,减少了通信开销。

WebSocket 原理

WebSocket 协议建立在 HTTP 协议之上,通过 HTTP 请求的升级机制(Upgrade)来升级为 WebSocket 连接。基本的连接过程如下:

客户端发起 WebSocket 连接请求,与服务器建立 TCP 连接

服务器响应 WebSocket 握手请求,双方达成协议升级,完成连接建立。

建立连接后,客户端和服务器可以通过 WebSocket 消息进行双向通信。

连接可以一直保持开启,直到其中一方发送关闭连接请求。

WebSocket 连接是一个持久连接,使用固定的套接字地址(ws:// 或 wss://)和端口号(通常是 80 或 443)。WebSocket 消息使用一种轻量级的帧格式,减少了通信开销。这使得 WebSocket 适用于需要实时通信的应用,如在线聊天、实时数据更新、在线游戏等。

在 Java 中,WebSocket 库通常会处理 WebSocket 握手、消息编码和解码、连接管理等底层细节,开发者可以专注于应用层的逻辑。WebSocket 协议的双向通信模型使其成为实时通信应用的理想选择,它在实时性要求高的场景中有广泛的应用。

springboot整合websocket过程

pom文件

java 复制代码
<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version> <!-- 使用正确的版本号 -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springws</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springws</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>

基本依赖

java 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.3.12</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>9.0.55</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>

系统配置

java 复制代码
server.port=8080
# WebSocket
websocket.address=ws://localhost:8080

配置文件

java 复制代码
@Configuration
public class WebSocketConfig {

    @Value("${ws://localhost:8080}")
    private String websocketAddress;

    /**
     * 注入ServerEndpointExporter,
     * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

Wbsocket操作类

java 复制代码
package com.hs.websocket;

import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")  // 接口路径 ws://localhost:8087/webSocket/userId;
public class WebSocket {

    private Session session;
    private String userId;
    private static Map<String, Session> sessionPool = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        sessionPool.put(userId, session);
        log.info("&#8203;``【oaicite:3】``&#8203;有新的连接,总数为: {}", sessionPool.size());
    }

    @OnClose
    public void onClose() {
        sessionPool.remove(userId);
        log.info("&#8203;``【oaicite:2】``&#8203;连接断开,总数为: {}", sessionPool.size());
    }

    @OnMessage
    public void onMessage(String messageText) {
        log.info("&#8203;``【oaicite:1】``&#8203;收到客户端消息: {}", messageText);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误, 原因: {}", error.getMessage());
    }

    public void sendAllMessage(String message) {
        log.info("&#8203;``【oaicite:0】``&#8203;广播消息: {}", message);
        sessionPool.values().forEach(this::sendMessage);
    }

    public void sendOneMessage(String targetUserId, String message) {
        Session session = sessionPool.get(targetUserId);
        if (session != null && session.isOpen()) {
            sendMessage(session, message);
        }
    }

    public void sendMoreMessage(String[] targetUserIds, String message) {
        for (String targetUserId : targetUserIds) {
            Session session = sessionPool.get(targetUserId);
            if (session != null && session.isOpen()) {
                sendMessage(session, message);
            }
        }
    }

    private void sendMessage(Session targetSession, String message) {
        try {
            targetSession.getAsyncRemote().sendText(message);
        } catch (IOException e) {
            log.error("发送消息失败: {}", e.getMessage());
        }
    }
}

前端测试

java 复制代码
<!DOCTYPE HTML>
<html>
<head>
    <title>Test My WebSocket</title>
</head>


<body>
TestWebSocket
<input id="text" type="text"/>
<button onclick="send()">SEND MESSAGE</button>
<button onclick="closeWebSocket()">CLOSE</button>
<div id="message"></div>
</body>

<script type="text/javascript">
    var websocket = null;


    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        //连接WebSocket节点
        websocket = new WebSocket("ws://localhost:8080/websocket/88");
    } else {
        alert('Not support websocket')
    }


    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("error");
    };


    //连接成功建立的回调方法
    websocket.onopen = function (event) {
        setMessageInnerHTML("open");
    }


    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        setMessageInnerHTML(event.data);
    }


    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("close");
    }


    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        websocket.close();
    }


    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }


    //关闭连接
    function closeWebSocket() {
        websocket.close();
    }


    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

postman测试

创建新的workspaces

通过ctrl+n进入

基于以下url进行联通测试

java 复制代码
ws://localhost:8080/websocket/88
相关推荐
忒可君6 分钟前
C# winform 报错:类型“System.Int32”的对象无法转换为类型“System.Int16”。
java·开发语言
斌斌_____22 分钟前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@31 分钟前
Spring如何处理循环依赖
java·后端·spring
一个不秃头的 程序员1 小时前
代码加入SFTP JAVA ---(小白篇3)
java·python·github
丁总学Java1 小时前
--spring.profiles.active=prod
java·spring
苹果醋31 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
上等猿1 小时前
集合stream
java
java1234_小锋1 小时前
MyBatis如何处理延迟加载?
java·开发语言
菠萝咕噜肉i1 小时前
MyBatis是什么?为什么有全自动ORM框架还是MyBatis比较受欢迎?
java·mybatis·框架·半自动
林的快手2 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode