springboot +websocket 不同的实现方案

这篇具有很好参考价值的文章主要介绍了springboot +websocket 不同的实现方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

序言

介绍了 netty方式 stomp方式netty-socketio等方式使用websocket协议
文章结尾还有在线客服案例

1.spring-boot-starter-websocket 方式

1.1引入依赖

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

1.2配置类

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
	private final HttpAuthHandler httpAuthHandler;
	private final CustomInterceptor customInterceptor;
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry
				.addHandler(httpAuthHandler, "testWs")
				.addInterceptors(customInterceptor)
				.setAllowedOrigins("*");
	}
	  @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxSessionIdleTimeout(3600000L);
        return container;
    }
}

1.3 WsSessionManager

@Slf4j
public class WsSessionManager {
	/**
	 * 保存连接 session 的地方
	 */
	private static ConcurrentHashMap<String, WebSocketSession> SESSION_POOL = new ConcurrentHashMap<>();

	/**
	 * 添加 session
	 *
	 * @param key
	 */
	public static void add(String key, WebSocketSession session) {
		// 添加 session
		SESSION_POOL.put(key, session);
	}

	/**
	 * 删除 session,会返回删除的 session
	 *
	 * @param key
	 * @return
	 */
	public static WebSocketSession remove(String key) {
		// 删除 session
		return SESSION_POOL.remove(key);
	}

	/**
	 * 删除并同步关闭连接
	 *
	 * @param key
	 */
	public static void removeAndClose(String key) {
		WebSocketSession session = remove(key);
		if (session != null) {
			try {
				// 关闭连接
				session.close();
			} catch (IOException e) {
				// todo: 关闭出现异常处理
				e.printStackTrace();
			}
		}
	}

	/**
	 * 获得 session
	 *
	 * @param key
	 * @return
	 */
	public static WebSocketSession get(String key) {
		// 获得 session
		return SESSION_POOL.get(key);
	}
}

1.4HttpAuthHandler

@Component
public class HttpAuthHandler extends TextWebSocketHandler {
	/**
	 * socket 建立成功事件
	 *
	 * @param session
	 * @throws Exception
	 */
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		Object token = session.getAttributes().get("token");
		if (token != null) {
			// 用户连接成功,放入在线用户缓存
			WsSessionManager.add(token.toString(), session);
		} else {
			throw new RuntimeException("用户登录已经失效!");
		}
	}

	/**
	 * 接收消息事件
	 *
	 * @param session
	 * @param message
	 * @throws Exception
	 */
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		// 获得客户端传来的消息
		String payload = message.getPayload();
		Object token = session.getAttributes().get("token");
		System.out.println("server 接收到 " + token + " 发送的 " + payload);
		session.sendMessage(new TextMessage("server 发送给 " + token + " 消息 " + payload + " " + LocalDateTime.now().toString()));
	}

	/**
	 * socket 断开连接时
	 *
	 * @param session
	 * @param status
	 * @throws Exception
	 */
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		Object token = session.getAttributes().get("token");
		if (token != null) {
			// 用户退出,移除缓存
			WsSessionManager.remove(token.toString());
		}
	}
}

1.5CustomInterceptor

@Component
public class CustomInterceptor implements HandshakeInterceptor {
	/**
	 * 握手前
	 *
	 * @param request
	 * @param response
	 * @param wsHandler
	 * @param attributes
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
		System.out.println("握手开始");

		// 获得请求参数
		Map<String, String> paramMap =  HttpUtil.decodeParamMap(request.getURI().getQuery(), CharsetUtil.charset("utf-8"));
		String uid = paramMap.get("token");
		if (StrUtil.isNotBlank(uid)) {
			// 放入属性域
			attributes.put("token", uid);
			System.out.println("用户 token " + uid + " 握手成功!");
			return true;
		}
		System.out.println("用户登录已失效");
		return false;
	}

	/**
	 * 握手后
	 *
	 * @param request
	 * @param response
	 * @param wsHandler
	 * @param exception
	 */
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
		System.out.println("握手完成");
	}
}

1.6 示例

springboot +websocket 不同的实现方案,Java,spring boot,websocket,java

2.io.netty.netty-all 方式

2.1netty介绍

  • netty定义

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
官网:https://netty.io/wiki/user-guide-for-4.x.html
更多样例
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example

  • 1.2netty优点
  • API使用简单,学习成本低。
  • 功能强大,内置了多种解码编码器,支持多种协议。
  • 性能高,对比其他主流的NIO框架,Netty的性能最优。
  • 社区活跃,发现BUG会及时修复,迭代版本周期短,不断加入新的功能。
  • Dubbo、Elasticsearch都采用了Netty,质量得到验证。
  • 1.3逻辑图
    springboot +websocket 不同的实现方案,Java,spring boot,websocket,java

BossGroup 和 WorkerGroup:

bossGroup 和 workerGroup 是两个线程池, 它们默认线程数为 CPU 核心数乘以 2 bossGroup
用于接收客户端传过来的请求,接收到请求后将后续操作交由 workerGroup 处理

Selector(选择器):

检测多个通道上是否有事件的发生
TaskQueue
(任务队列):

上面的任务都是在当前的 NioEventLoop ( 反应器 Reactor 线程 ) 中的任务队列中排队执行 ,
在其它线程中也可以调度本线程的 Channel 通道与该线程对应的客户端进行数据读写
Channel:

Channel 是框架自己定义的一个通道接口, Netty 实现的客户端 NIO 套接字通道是 NioSocketChannel
提供的服务器端 NIO 套接字通道是 NioServerSocketChannel
当服务端和客户端建立一个新的连接时, 一个新的 Channel 将被创建,同时它会被自动地分配到它专属的 ChannelPipeline :
ChannelPipeline
是一个拦截流经 Channel 的入站和出站事件的 ChannelHandler 实例链,并定义了用于在该链上传播入站和出站事件流的 API
ChannelHandler:

分为 ChannelInBoundHandler 和 ChannelOutboundHandler 两种 如果一个入站 IO
事件被触发,这个事件会从第一个开始依次通过 ChannelPipeline中的 ChannelInBoundHandler,先添加的先执行。
若是一个出站 I/O 事件,则会从最后一个开始依次通过 ChannelPipeline 中的
ChannelOutboundHandler,后添加的先执行,然后通过调用在 ChannelHandlerContext
中定义的事件传播方法传递给最近的 ChannelHandler。 在 ChannelPipeline 传播事件时,它会测试
ChannelPipeline 中的下一个 ChannelHandler 的类型是否和事件的运动方向相匹配。
如果某个ChannelHandler不能处理则会跳过,并将事件传递到下一个ChannelHandler,直到它找到和该事件所期望的方向相匹配的为止。

2.2整合

  • 2.1maven依赖
<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
		</dependency>
  • 2.2配置类
/**
 * @Author: GZ
 * @CreateTime: 2023-03-20  11:51
 * @Description: 
 * @Version: 1.0
 */
@Component
public class WebSocketChannelConfig  extends ChannelInitializer<Channel> {
    @Resource
    private  WebSocketFrameHandler webSocketFrameHandler;
    @Override
    protected void initChannel(Channel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        // netty中http协议的编解码
        pipeline.addLast(new HttpServerCodec());
        //最大内容长度
        pipeline.addLast(new HttpObjectAggregator(65536));
        //压缩
        pipeline.addLast(new WebSocketServerCompressionHandler());
        //协议
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws", null, true));
        //主要逻辑
        pipeline.addLast(webSocketFrameHandler);
    }
}
  • 2.3WebSocketServer
package com.insound.commontest.component;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @Author: GZ
 * @CreateTime: 2023-03-20  10:45
 * @Description: WebSocketServer
 * @Version: 1.0
 */
@Component
@Slf4j
public class WebSocketServer {

    @Autowired
    private static WebSocketChannelConfig webSocketChannelConfig;

    @PostConstruct
    private void init() {
        bind(8084);
    }

    public static void bind(int port) {
        //老板线程,用于接受线程
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //工人线程,用于处理任务
        EventLoopGroup workerGroup = new NioEventLoopGroup(1);
        try {
            //创建netty启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //设置线程组
            serverBootstrap.group(bossGroup, workerGroup)
            //设置通道非阻塞IO
            .channel(NioServerSocketChannel.class)
            //设置日志
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(webSocketChannelConfig);
            //服务器异步创建绑定通道
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            log.info("-----netty服务器端启动成功-----");
            //关闭服务器通道
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("WebSocketServer is error",e);

        } finally {
            //释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
  • 2.4WebSocketFrameHandler
@Component
@Slf4j
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {


    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        log.info("接受消息");
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("建立连接");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("断开连接");
    }
}

3.netty-socketio 方式

3.1依赖

<dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>${netty-socketio}</version>
        </dependency>

3.2配置

package com.gz.im.backend.config;

import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import com.gz.im.backend.exception.InstantMessageExceptionListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author GuoZhong
 * @description netty socketIo配置类
 * @date 2023/3/26 22:31
 */
@Configuration
public class NettySocketIoConfig {

	@Value("${socketIo.host}")
	private String host;

	@Value("${socketIo.port}")
	private Integer port;

	@Value("${socketIo.bossCount}")
	private int bossCount;

	@Value("${socketIo.workCount}")
	private int workCount;

	@Value("${socketIo.allowCustomRequests}")
	private boolean allowCustomRequests;

	@Value("${socketIo.upgradeTimeout}")
	private int upgradeTimeout;

	@Value("${socketIo.pingTimeout}")
	private int pingTimeout;

	@Value("${socketIo.pingInterval}")
	private int pingInterval;

	@Value("${socketIo.maxFramePayloadLength}")
	private int maxFramePayloadLength;

	@Value("${socketIo.maxHttpContentLength}")
	private int maxHttpContentLength;


	@Bean
	public SocketIOServer socketIOServer() {

		com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
		// 开启Socket端口复用 解决对此重启服务时,netty端口被占用问题
		com.corundumstudio.socketio.SocketConfig socketConfig = new com.corundumstudio.socketio.SocketConfig();
		socketConfig.setReuseAddress(true);
		socketConfig.setTcpNoDelay(true);
		socketConfig.setSoLinger(0);
		config.setSocketConfig(socketConfig);
		//主机地址
		config.setHostname(host);
		//端口
		config.setPort(port);
		//老板线程
		config.setBossThreads(bossCount);
		//工作线程
		config.setWorkerThreads(workCount);
		//允许为不同于 socket.io 协议的自定义请求提供服务
		config.setAllowCustomRequests(allowCustomRequests);
		//异常监听
		config.setExceptionListener(new InstantMessageExceptionListener());
		config.setUpgradeTimeout(upgradeTimeout);
		config.setPingTimeout(pingTimeout);
		config.setPingInterval(pingInterval);
		config.setMaxHttpContentLength(maxHttpContentLength);
		config.setMaxFramePayloadLength(maxFramePayloadLength);
		return  new SocketIOServer(config);
	}
    /**
     * 扫描socket 注解类
     */
	@Bean
	public SpringAnnotationScanner springAnnotationScanner() {
		return new SpringAnnotationScanner(socketIOServer());
	}


}

3.2 application.yml

server:
  port: 8081

socketIo:
  # host主机
  host:  localhost
  # 端口
  port: 8082
  # 内容长度
  maxFramePayloadLength: 1048576
  # http请求长度
  maxHttpContentLength: 1048576
  # 老板线程
  bossCount: 1
  # 工作线程
  workCount: 100
  # 自定义请求 允许为不同于 socket.io 协议的自定义请求提供服务
  allowCustomRequests: true
  # 协议升级超时
  upgradeTimeout: 1000000
  # ping超时
  pingTimeout: 6000000
  # ping间隔
  pingInterval: 25000

3.3SocketIoServer

package com.gz.im.backend.component;
import com.corundumstudio.socketio.SocketIOServer;
import com.gz.im.backend.component.handler.ImEventHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;


/**
 * @author socket服务初始化与销毁
 */

@Component
@RequiredArgsConstructor
public class SocketIoServer  {
    private final SocketIOServer server;

  
	@PostConstruct
	public void init()  {
		server.addNamespace("/client").addListeners(new ImEventHandler());
		server.start();
	}
	@PreDestroy
	public void destroy() {
		server.stop();
	}
}

3.4ImEventHandler

package com.gz.im.backend.component.handler;


import cn.hutool.json.JSONUtil;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.gz.im.backend.pojo.MessageRequest;
import com.gz.im.backend.pojo.User;
import com.gz.im.backend.pojo.enums.TurnAgentTypeEnum;
import lombok.extern.slf4j.Slf4j;


/**
 * @author GuoZhong
 * @description 客户端nettySocket 处理类
 * @date 2023/3/26 22:50
 */

@Slf4j
public class ImEventHandler {

	private final ImEventProxy proxy;
	public ImEventHandler(ImEventProxy proxy) {
		this.proxy = proxy;
	}
    /**
     * 客户端连接
     */
	@OnConnect
	public void onConnect(SocketIOClient client) {
		log.info("im is connect");
	


	}
	/**
	 * 客户端发生消息
	 */
	@OnEvent("chat")
	public void chat(SocketIOClient client, AckRequest ackRequest, MessageRequest data) {
		log.info("客户端发来的数据:{}",data);
	
	}


	/**
	 * 客户端断开
	 */
	@OnDisconnect
	public void onDisconnect(SocketIOClient client) {
		log.info("im is disconnect");
		

	}


}

3.5示例

springboot +websocket 不同的实现方案,Java,spring boot,websocket,java

4.stomp方式

官网连接
https://docs.spring.io/spring-framework/docs/5.3.26/reference/html/web.html#websocket-stomp

springboot +websocket 不同的实现方案,Java,spring boot,websocket,java

springboot +websocket 不同的实现方案,Java,spring boot,websocket,java

4.1WebSocketConfig

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket").setAllowedOrigins("*")
                .setHandshakeHandler(new CustomHandshakeHandler())
                //用于浏览器兼容
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {

        //以 /app 开头的 STOMP 消息被路由到 @Controller 类中的 @MessageMapping 方法
        config.setApplicationDestinationPrefixes("/app");

        //topic 代表广播消息
        //queue代表 一对一消息
        config.enableSimpleBroker("/topic", "/queue");

    }

}

4.2CustomHandshakeHandler

package com.insound.commontest.component;

import com.insound.commontest.pojo.StompPrincipal;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

import java.security.Principal;
import java.util.Map;


/**
 * @Author: GZ
 * @CreateTime: 2023-03-23  13:50
 * @Description: TODO
 * @Version: 1.0
 */

public class CustomHandshakeHandler extends DefaultHandshakeHandler {

/**
     * 重写定义用户信息方法
     *
     * @param request    握手请求对象
     * @param wsHandler  WebSocket管理器,用于管理信息
     * @param attributes 用于传递WebSocket会话的握手属性
     * @return StompPrincipal 自定义用户信息
     */

    @Override
    protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        //获取客户端主机名称
        String hostName = request.getRemoteAddress().getHostName();
        //获取客户端主机IP地址
        String hostAddress = request.getRemoteAddress().getAddress().getHostAddress();
        //StompPrincipal(name = hostName, publicName = hostAddress)
        return new StompPrincipal(hostName, hostAddress);
    }

}

4.3 controller

   @Autowired
    public SimpMessagingTemplate template;

    /**
     * 广播
     *
     * @param msg
     */
    @ResponseBody
    @RequestMapping("/pushToAll")
    public void subscribe( @RequestBody String msg) {

        template.convertAndSend("/topic/all", msg);
    }

5.区别

  • netty-socketio
    性能高
    跨平台
    兼容所有浏览器
    内置心跳检测
  • springboot-websocket
    操作方便
  • netty
    性能高
  • stomp
    无需自定义消息传递协议和消息格式
    与消息中间无缝衔接
    内置心跳检测

6.在线客服案例

https://gitee.com/GZ-jelly/gz-im文章来源地址https://www.toymoban.com/news/detail-686647.html

到了这里,关于springboot +websocket 不同的实现方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 大华摄像头实时预览(spring boot+websocket+flv.js)Java开发

    1.大华NetSDK_JAVA; 这里使用的是 Linux64的架包 2.websocket 前端使用的vue框架    3.flv.js的播放插件     4.大华摄像头提供的平台(后面称为官方平台) 根据大华《NetSDK_JAVA编程指导手册》的流程图 根据图可以得知关键流程为: 初始化sdk——登录设备——打开实时预览——设置视

    2024年02月04日
    浏览(80)
  • Spring Boot进阶(49):实时通信不再是梦想,SpringBoot+WebSocket助你轻松实现前后端即时通讯!

            在上一期,我对WebSocket进行了基础及理论知识普及学习,WebSocket是一种基于TCP协议实现的全双工通信协议,使用它可以实现实时通信,不必担心HTTP协议的短连接问题。Spring Boot作为一款微服务框架,也提供了轻量级的WebSocket集成支持,本文将介绍如何在Spring Boot项

    2024年02月11日
    浏览(41)
  • SpringBoot(java)实现websocket实现实时通信

    WebSockets是一种在Web应用程序中实现实时通信的技术。它允许客户端和服务器之间建立持久的、双向的通信通道,从而使得服务器可以实时向客户端推送数据,而不需要客户端不断地向服务器发起请求。这种实时通信的能力对于需要即时更新数据的应用程序非常有用,比如在线

    2024年04月29日
    浏览(33)
  • Java21 + SpringBoot3使用spring-websocket时执行mvn package报错

    近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。 本项目为前后端分离开发,后端基于 Java21 和 SpringBoot3 开发,前端提供了vue、angular、react、uniap

    2024年02月02日
    浏览(55)
  • spring boot学习第六篇:SpringBoot 集成WebSocket详解

    1、WebSocket简介 WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。 2、为什么需要WebSocket HTTP 是基于请求响应式的,即通信只能由客户端发起,服务端做出响应,无状态,无连接。 无状态:每次连

    2024年01月21日
    浏览(40)
  • Java实现森林火灾预警系统 websocket+JAVA+Vue+SpringBoot+MySQL

    基于websocket+Vue+SpringBoot+MySQL的森林火灾预警系统,包含园区数据模块、温度传感器模块、烟雾传感器模块、园区监控模块、园区报警历史模块,还包含系统自带的用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典管理、文件管理、图表展示等基础模块,森林火

    2024年02月01日
    浏览(28)
  • Java spring boot 实现批量删除功能

    根据前面的删除的功能的实现,现在我们来实现一个可以批量删除的功能,不懂删除功能的可以跳到主页去搜索Java删除的功能即可,这里就不多进行啰嗦,那么进入正题! 原理: 批量删除的原理是通过一个循环来遍历要删除的用户信息的ID数组,然后依次调用单个删除矿工

    2024年02月12日
    浏览(41)
  • java上传实现 spring boot +element ui

    先从element ui el-upload组件开始介绍。 关于headers 在return里写,这个即可获得headers 目录 1、得到文件上传路径,getUploadPath方法 2、FileUploadUtils里的upload方法,返回一个新的文件名 3、获取url  完整的FileUploadUtils  再来看后台controller 大致步骤分为以下: 1、得到文件上传路径,

    2024年02月12日
    浏览(28)
  • Spring Boot进阶(48):SpringBoot之集成WebSocket及使用说明 | 超级详细,建议收藏

            WebSocket是一种新型的通信协议,它可以在客户端与服务器端之间实现双向通信,具有低延迟、高效性等特点,适用于实时通信场景。在SpringBoot应用中,集成WebSocket可以方便地实现实时通信功能,如即时聊天、实时数据传输等。         本文将介绍如何在Sprin

    2024年02月16日
    浏览(40)
  • Spring Boot 实现 WebSocket 示例

    WebSocket协议提供了一种标准化的方法,通过单个TCP连接在客户机和服务器之间建立全双工、双向的通信通道。它是一种不同于HTTP的TCP协议,但被设计为在HTTP上工作,使用端口80和443,并允许重用现有的防火墙规则。 WebSocket 协议是独立的基于 TCP 协议。它与 HTTP 的唯一关系是

    2024年02月14日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包