01-websocket初始

springboot 整合 javax.websocket示例

前言:本文简单记录在spring-boot项目中使用websocket

一 、javax.websocket介绍

当今互联网应用程序越来越注重实时性和双向通信,而传统的HTTP协议在这方面的表现相对较弱。WebSocket(Web套接字)是一种在单个TCP连接上实现全双工通信的协议,它允许在浏览器和服务器之间建立持久性的、实时的通信连接,而无需不断发起新的HTTP请求。

特点:

  1. 全双工通信: WebSocket允许在同一个连接上实现双向的实时通信,服务器和客户端可以同时发送和接收数据,而不需要等待对方的响应。

  2. 持久连接: 传统的HTTP请求-响应模式需要在每次通信时重新建立连接,而WebSocket连接在建立后会一直保持开放状态,从而减少了连接建立的开销。

  3. 低延迟: WebSocket的持久性连接和全双工通信特性使得数据能够更快地传输,从而降低了通信的延迟。

  4. 轻量级: WebSocket的协议头相对较小,减少了数据传输的开销。

  5. 跨域支持: 通过CORS(跨源资源共享)机制,WebSocket允许不同域名下的网页与服务器进行实时通信。

工作原理:

  1. 握手阶段: WebSocket的连接始于一个HTTP请求,这个请求称为WebSocket握手。客户端向服务器发起一个带有特殊头部的HTTP请求,请求表明客户端希望升级连接为WebSocket连接。如果服务器支持WebSocket,它会响应一个HTTP 101状态码,表示握手成功,此后连接升级为WebSocket连接。

  2. 数据传输: 握手成功后,WebSocket连接会一直保持开放状态。在任何一方需要发送数据时,数据会被封装为WebSocket帧,并通过已建立的连接进行传输。帧可以是文本数据或二进制数据。

  3. 关闭连接: 要关闭WebSocket连接,任意一方可以发送一个关闭帧。收到关闭帧后,另一方也会发送一个关闭帧作为确认。连接会在双方都确认关闭后断开。

使用场景:
WebSocket适用于需要实时更新数据的应用场景,例如:

  • 即时聊天应用
  • 实时协作工具
  • 实时数据可视化
  • 在线游戏
  • 实时通知和推送服务

总之,WebSocket是一种能够提供低延迟、实时、双向通信的协议,为现代Web应用程序提供了强大的实时性能。

二、示例代码

首先是依赖部分,主要包括springBoot、spring-web-socket,解析json,用到了fastJSON

     <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.3</version>
    </parent>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

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

		 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

然后是java代码部分,这里模拟最简单的,一个配置类提供 ServerEndpointExporter
一个组件类,实现websocket的握手和接发消息,以及处理异常

配置类的代码

@Configuration
public class WebSocketConfig implements WebMvcConfigurer {
    @Bean
    public ServerEndpointExporter serverEndpointExporter (){
        return new ServerEndpointExporter();
    }
}

组件类的代码,主要有3个方法,建立连接、断开连接以及接收消息及转发消息的方法,最后还有一个出现错误时的处理方法

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.config.MessageConfigurator;
import org.example.domain.entity.UserSession;
import org.example.domain.vo.AbstractMessage;
import org.example.domain.vo.PersonMessage;
import org.example.domain.vo.SystemMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 类描述
 *
 * @author cjia
 * @date 2023/8/28 下午 10:50
 */

@ServerEndpoint(value = "/message/{username}")
@Component
public class GMessageListener {
    public static ConcurrentMap<String, UserSession> sessions = new ConcurrentHashMap<>();
    private static Logger logger = LoggerFactory.getLogger(GMessageListener.class) ;

    private String username ;


 /**
     * 建立连接时触发的方法
     *
     * @param session  会话
     * @param config   配置
     * @param username 用户名
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config, @PathParam("username") String username){
        UserSession userSession = new UserSession(session.getId(), username, session) ;
        this.username = username ;
        sessions.put(username, userSession) ;
        logger.info("【{}】用户进入, 当前连接数:{}", username + ":" + session.getId(), sessions.size()) ;
    }

     /**
     * 关闭连接时触发的方法
     *
     * @param session 会话
     * @param reason  原因
     */
    @OnClose
    public void onClose(Session session, CloseReason reason){
        UserSession userSession = sessions.remove(this.username) ;
        if (userSession != null) {
            logger.info("用户【{}】, 断开连接, 当前连接数:{}", username, sessions.size()) ;
        }
    }

    /**
     * 接收消息触发的方法
     *
     * @param session 会话
     * @param message 消息
     * @throws IOException ioexception
     */
    @OnMessage
    public void onMessage(Session session, String message) throws IOException {
        //根据实际的情况书写逻辑,我这里传递的是json字符串
        //  { "type":"system", "content":"测试" }
        logger.info("接受到消息:" + message);
        JSONObject jsonObject = JSONObject.parseObject(message);
        logger.info("解析的内容:{}", jsonObject.get("type"));
        logger.info("解析的内容:{}", jsonObject.get("content"));
        SystemMessage systemMessage = jsonObject.toJavaObject(SystemMessage.class);
        logger.info("解析之后转换:{}", systemMessage);
        //可以单独封装发送消息的方法,这里只是简单使用
        session.getBasicRemote().sendText("消息收到: "+ message);
        //这个是异步调用,但是这个异步是不同源,才能异步,也就是不同的user,如果是同一个user,只能同步
        //session.getAsyncRemote().sendText(content);
    }

    /**
     * 出现错误时触发的方法
     *
     * @param session 会话
     * @param error   错误
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error(error.getMessage()) ;
    }
}

三、使用apiPost7建立websocket连接

img

控制台部分日志输出

2023-08-31 22:33:32.169  INFO 13476 --- [nio-8090-exec-1] org.example.service.GMessageListener     : 【1:0】用户进入, 当前连接数:1
2023-08-31 22:33:34.061  INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener     : 接受到消息:{
    "type":"system",
    "content":"测试"
}
2023-08-31 22:33:34.094  INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener     : 解析的内容:system
2023-08-31 22:33:34.094  INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener     : 解析的内容:测试
2023-08-31 22:33:34.099  INFO 13476 --- [nio-8090-exec-2] org.example.service.GMessageListener     : 解析之后转换:SystemMessage()
2023-08-31 22:33:35.757  INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener     : 接受到消息:{
    "type":"system",
    "content":"测试"
}
2023-08-31 22:33:35.757  INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener     : 解析的内容:system
2023-08-31 22:33:35.757  INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener     : 解析的内容:测试
2023-08-31 22:33:35.757  INFO 13476 --- [nio-8090-exec-3] org.example.service.GMessageListener     : 解析之后转换:SystemMessage()
2023-08


2023-08-31 22:48:53.786  INFO 13476 --- [nio-8090-exec-9] org.example.service.GMessageListener     : 【2:1】用户进入, 当前连接数:2

四、springframework.web.socket和 javax.websocket 的区别

springframework.web.socketjavax.websocket 都是与 WebSocket 相关的 Java 技术,但它们有一些区别。以下是它们之间的主要区别:

  1. 来源和归属:

    • javax.websocket 是 Java API for WebSocket 的一部分,是 Java EE 标准规范的一部分。它提供了一组标准的接口和类,用于开发 WebSocket 应用。
    • springframework.web.socket 则是 Spring 框架提供的 WebSocket 模块,它是在 javax.websocket 的基础上构建的,并且提供了对 WebSocket 的更高层次的抽象和额外的功能,以便更容易地集成到 Spring 应用中。
  2. 抽象级别:

    • javax.websocket 提供了较低层次的 API,开发者需要直接与 WebSocket 协议打交道。
    • springframework.web.socket 提供了更高层次的抽象,简化了 WebSocket 的使用,并且与 Spring 框架的其他部分更好地集成。
  3. 功能和特性:

    • springframework.web.socket 提供了一些额外的功能,如与 Spring Security 集成、消息拦截、广播等。它还支持 STOMP(Simple Text Oriented Messaging Protocol)协议,用于在 WebSocket 上实现更高级别的消息传递。
    • javax.websocket 是一个相对较基础的 API,功能上相对较少,更专注于提供 WebSocket 的核心功能。
  4. 依赖关系:

    • 使用 javax.websocket 时,你只需依赖 Java EE 的规范和相应的实现。
    • 使用 springframework.web.socket 时,需要引入 Spring 框架的相关模块,因此可能有一些额外的依赖。
      于在 WebSocket 上实现更高级别的消息传递。
    • javax.websocket 是一个相对较基础的 API,功能上相对较少,更专注于提供 WebSocket 的核心功能。
  5. 依赖关系:

    • 使用 javax.websocket 时,你只需依赖 Java EE 的规范和相应的实现。
    • 使用 springframework.web.socket 时,需要引入 Spring 框架的相关模块,因此可能有一些额外的依赖。
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值