SpringBoot整合Netty+Websocket实现消息推送

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

前言

       Netty是一个高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。以下是Netty的主要优势:

  1. 高性能:Netty基于NIO(非阻塞IO)模型,采用事件驱动的设计,具有高性能的特点。它通过零拷贝技术、内存池化技术等手段,进一步提高了IO性能,降低了资源消耗。
  2. 易用性:Netty提供了丰富的API和功能,如对TCP、UDP和文件传输的支持,以及对SSL/TLS、压缩、编解码等功能的内置实现。这些功能简化了网络应用的开发,降低了学习和使用的难度。
  3. 可扩展性:Netty采用了模块化设计,各个模块之间耦合度低,易于扩展。开发者可以根据需要定制和扩展Netty的功能,如添加新的编解码器、处理器或协议。
  4. 稳定性:Netty经过了大规模生产环境的验证,具有高稳定性。它通过合理的线程模型、资源管理和错误处理机制,保证了系统的稳定性和可靠性。
  5. 社区活跃:Netty拥有一个活跃的开源社区,不断有新的功能和优化被贡献出来。这为开发者提供了强大的支持,也促进了Netty的发展和完善。
  6. 跨平台性:Netty可以在多种操作系统和平台上运行,如Windows、Linux和Mac OS等。这一特性使得开发者可以轻松地在不同环境下部署和维护网络应用。

        WebSocket 是一种网络通信协议,相比传统的HTTP协议,它具有以下优势: 

  1. 实时性:WebSocket 允许服务器主动向客户端推送数据,从而实现实时通信,这对于需要即时反馈的应用(如在线游戏、聊天应用等)至关重要。
  2. 全双工通信:WebSocket 支持双向通信,服务器和客户端可以同时发送和接收数据,提高了通信的灵活性。
  3. 节省带宽:由于 WebSocket 在单个 TCP 连接上运行,避免了为每个消息创建新连接所需的额外开销,减少了数据传输量。
  4. 更好的二进制支持:WebSocket 定义了二进制帧,可以更轻松地处理二进制内容,如图片、音视频等。
  5. 跨域通信:WebSocket 支持跨域通信,使得客户端可以与不同域名的服务器进行通信,增加了应用的灵活性和可访问性。
  6. 可扩展性:WebSocket 定义了扩展机制,用户可以根据需要扩展协议或实现自定义的子协议。
  7. 更好的支持实时应用:由于 WebSocket 的实时性和全双工通信特性,它特别适合用于需要实时反馈的应用,例如在线游戏、实时音视频聊天等。
  8. 更快的传输速度:由于 WebSocket 减少了不必要的连接和状态转换,通信速度更快。
  9. 更低的延迟:由于 WebSocket 建立的是持久连接,减少了建立和关闭连接的开销,从而降低了通信延迟。
  10. 更强的兼容性:虽然 WebSocket 协议并未在所有浏览器中得到完全支持,但有各种库和框架可以帮助实现兼容性,例如通过 polyfill 技术。

说明:以下为SpringBoot整合Netty+Websocket实现实时的消息通讯

一、引入Netty依赖

   <!--netty-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>

二、 使用步骤

1.配置服务启动类

package com.pzg.chat.communication;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Slf4j
@Component
public class WebSocketNettyServer {

	@Autowired
	WebSocketChannelInitializer webSocketChannelInitializer;
	/**
	 * Netty服务器启动对象
	 */
	private  final ServerBootstrap serverBootstrap = new ServerBootstrap();;

	@PostConstruct
	public void WebSocketNettyServerInit() {
		// 初始化服务器启动对象
		// 主线程池
		NioEventLoopGroup mainGrp = new NioEventLoopGroup();
		// 从线程池
		NioEventLoopGroup subGrp = new NioEventLoopGroup();
		serverBootstrap
				// 指定使用上面创建的两个线程池
				.group(mainGrp, subGrp)
				// 指定Netty通道类型
				.channel(NioServerSocketChannel.class)
				// 指定通道初始化器用来加载当Channel收到事件消息后
				.childHandler(webSocketChannelInitializer);
	}

	public void start() throws InterruptedException {
		// 绑定服务器端口,以异步的方式启动服务器
		ChannelFuture future = serverBootstrap.bind("0.0.0.0",8089).sync();
		if (future.isSuccess()){
			log.info("netty初始化完成,端口8088");
		}
	}
}

        说明:@PostConstruct用来保证容器初始化后触发该注解下的方法

 2.Netty服务初始化器

package com.pzg.chat.communication;

import com.pzg.chat.handler.ChatHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


@Component
public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {

	@Autowired
	private ChatHandler chatHandler;

	@Override
	protected void initChannel(SocketChannel socketChannel) {
		//获取对应的管道
		ChannelPipeline pipeline = socketChannel.pipeline();

		pipeline
				//添加HTTP编码解码器
				.addLast(new HttpServerCodec())
				//添加对大数据流的支持
				.addLast(new ChunkedWriteHandler())
				//添加聚合器
				.addLast(new HttpObjectAggregator(1024 * 64))
				//设置websocket连接前缀前缀
				//心跳检查(8秒)
				.addLast(new IdleStateHandler(8,0,0))
				//添加自定义处理器
				.addLast(new WebSocketServerProtocolHandler("/ws",null,true))
				.addLast(chatHandler);

	}
}

3.自定义处理器类

package com.pzg.chat.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pzg.chat.communication.context.impl.WebSocketContext;
import com.pzg.chat.service.ChannelService;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@ChannelHandler.Sharable
@SuppressWarnings("all")
@Component
@Slf4j
public class ChatHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

	private static ChatHandler chatHandler;

	@Autowired
	private ChannelService channelService;


	@Autowired
	private WebSocketContext webSocketContext;


	@PostConstruct
	public void init() {
		chatHandler = this;
	}

	/**
	 * 创建ChannelGroup对象存储所有连接的用户
	 */
	private static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

	/**
	 * 有新消息时会调用这个方法
	 *
	 * @param channelHandlerContext 上下文处理器
	 * @param textWebSocketFrame    文本
	 * @throws Exception
	 */
	@Override
	@SuppressWarnings("all")
	public void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
		System.out.println(frame.getClass());
		if (frame instanceof FullHttpRequest) {
		}
		//判断是否为关闭事件
		if (frame instanceof CloseWebSocketFrame) {
			ctx.channel().close();
			return;
		}
		if (frame instanceof PingWebSocketFrame) {
			return;
		}
		if (frame instanceof TextWebSocketFrame) {
			TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) frame;
			JSONObject jsonObject = JSON.parseObject(textWebSocketFrame.text());
			webSocketContext.executeWebSocketContext(jsonObject,ctx.channel());
			//类型转为(前后端达成的消息体)
		}
		//遍历出所有连接的通道
	}

	/**
	 * 有新的连接建立时
	 *
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		//加入通道组
		clients.add(ctx.channel());
	}

	/**
	 * 不活跃时会调用这个方法
	 *
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		//移除出通道组
		try {
			channelService.deleteBindUserIdAndIdChannel(ctx.channel().id().asShortText());
			channelService.deleteChannelBindUserId(ctx.channel());
		}catch (Exception e){

		}
		clients.remove(ctx.channel());
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		// 获取参数
		super.channelActive(ctx);
	}

	//检查客户端写心跳事件
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		Channel channel = ctx.channel();
		if (evt instanceof IdleStateEvent) {
			IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
			if (idleStateEvent.state() == IdleState.READER_IDLE) {
				try {
					channelService.deleteBindUserIdAndIdChannel(ctx.channel().id().asShortText());
					channelService.deleteChannelBindUserId(ctx.channel());
				}catch (Exception e){

				}
				clients.remove(channel);
				channel.close();
			}
		} else {
			super.userEventTriggered(ctx, evt);
		}
	}


}

        说明:webSocketContext.executeWebSocketContext(jsonObject,ctx.channel()); 为自己定义的处理消息的类,textWebSocketFrame.text()为对应的消息,可自行处理

4.缓存用户Channel接口和对应实现类

1.接口

package com.pzg.chat.service;

import io.netty.channel.Channel;

public interface ChannelService {

	void setUserIdBindChannel(Channel channel);

	void setIdBindChannel(String id,Channel channel);

	void setChannelBindUserId(Channel channel);

	String getChannelBindUserId(Channel channel);

	void deleteChannelBindUserId(Channel channel);

	void deleteBindUserIdChannel();

	void deleteBindIdChannel(String id);

	void setUserIdAndIdBindChannel(String id ,Channel channel);

	void deleteBindUserIdAndIdChannel(String id);

	Channel getUserIdChannel(String userId);

	Channel getIdBindChannel(String Id);
}

2.实现类 

package com.pzg.chat.service.impl;
import com.pzg.chat.service.ChannelService;
import com.pzg.chat.utils.UserUtil;
import io.netty.channel.Channel;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class ChannelServiceImpl implements ChannelService {

	//保存用户id和channel的映射
	public static HashMap<String,Channel> userIdChannel = new HashMap<>();
	//保存channelId和channel映射关系
	public static HashMap<String,Channel> idChannel = new HashMap<>();
	//保存channel和userID映射关系
	public static HashMap<Channel,String> ChannelUserId = new HashMap<>();

	@Override
	public void setUserIdBindChannel(Channel channel) {
		String userId = String.valueOf(UserUtil.getUserDetailsDTO().getId());
		userIdChannel.put(userId,channel);
	}

	@Override
	public void setIdBindChannel(String id, Channel channel) {
		idChannel.put(id,channel);
	}

	@Override
	public void setChannelBindUserId(Channel channel) {
		String userId = String.valueOf(UserUtil.getUserDetailsDTO().getId());
		System.out.println("----------------------->"+userId);
		ChannelUserId.put(channel,userId);
	}

	@Override
	public String getChannelBindUserId(Channel channel) {
		return ChannelUserId.get(channel);
	}

	@Override
	public void deleteChannelBindUserId(Channel channel) {
		ChannelUserId.remove(channel);
	}

	@Override
	public void deleteBindUserIdChannel() {
		String userId = String.valueOf(UserUtil.getUserDetailsDTO().getId());
		userIdChannel.remove(userId);
	}

	@Override
	public void deleteBindIdChannel(String id) {
		idChannel.remove(id);
	}

	@Override
	public void setUserIdAndIdBindChannel(String id, Channel channel) {
		setUserIdBindChannel(channel);
		setIdBindChannel(id,channel);
	}

	@Override
	public void deleteBindUserIdAndIdChannel(String id) {
		deleteBindIdChannel(id);
		deleteBindUserIdChannel();
	}

	@Override
	public Channel getUserIdChannel(String userId) {
		return userIdChannel.get(userId);
	}

	@Override
	public Channel getIdBindChannel(String Id) {
		return idChannel.get(Id);
	}

}

        说明:缓存Channel主要保证消息能发送到对应的Channel中,消息可携带用户id通过id查找Channel,将信息存入即可 ,通过channel.writeAndFlush(new TextWebSocketFrame("消息内容"));刷出消息。

5.调用start()启动Netty服务

package com.pzg.chat.listener;

import com.pzg.chat.communication.WebSocketNettyServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class NettyStartListener implements ApplicationListener<ContextRefreshedEvent> {

	/**
	 * 注入启动器
	 */
	@Autowired
	private WebSocketNettyServer webSocketNettyServer;

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		//判断event上下文中的父级是否为空
		if (event.getApplicationContext().getParent() == null) {
			try {
				//为空则调用start方法
				webSocketNettyServer.start();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

6.Websocket配置

// 导出socket对象
import {getToken} from '@/utils/auth';
export {
    socket
}
import { Message } from 'element-ui'
import {header} from "../../listening/header";
import {asidefriend} from "../../listening/asidefriend";
import {chatbox} from "../../listening/chatbox";
import {chatcontent} from "../../listening/chatcontent";
import {videocalls} from "../../listening/videocalls";
import {voicecalls} from "../../listening/voicecalls";
// socket主要对象
var socket = {
    websock: null,
    ws_url: "ws://localhost:8089/ws",
    /**
     * 开启标识
     * */
    socket_open: false,
    /**
     * 心跳timer
     * */
    hearbeat_timer: null,
    /**
     * 心跳发送频率
     * */
    hearbeat_interval: 5000,
    /**
     * 是否开启重连
     * */
    is_reonnect: true,
    /**
     * 重新连接的次数
     * */
    reconnect_count: 3,
    /**
     * 当前重新连接的次数,默认为:1
     * */
    reconnect_current: 1,
    /**
     * 重新连接的时间类型
     * */
    reconnect_timer: null,
    /**
     * 重新连接的间隔
     * */
    reconnect_interval: 3000,

      i : 0,

     timer:null,
    /**
     * 初始化连接
     */
    init: () => {
        if (!("WebSocket" in window)) {
            Message({
                message: '当前浏览器与网站不兼容丫',
                type: 'error',
            });
            return null
        }

        if (socket.websock && socket.websock.readyState===1) {
            return socket.websock
        }

        socket.websock = new WebSocket(socket.ws_url)
        //接收消息
        socket.websock.onmessage = function (e) {
            //调用处理消息的方法
            socket.receive(e)
        }

        // 关闭连接
        socket.websock.onclose = function (e) {
            clearInterval(socket.hearbeat_interval);
            socket.socket_open = false;
            if (socket.websock!=null){
                header.getWebsocketStatus(socket.websock.readyState);
            }
            // 需要重新连接
            if (socket.is_reonnect) {
                socket.reconnect_timer = setTimeout(() => {
                    // 超过重连次数
                    if (socket.reconnect_current > socket.reconnect_count) {
                        clearTimeout(socket.reconnect_timer)
                        return
                    }
                    // 记录重连次数
                    socket.reconnect_current++
                    socket.reconnect()
                }, socket.reconnect_interval)
            }
        }

        // 连接成功
        socket.websock.onopen = function () {
            // Message({
            //     message: '连接成功',
            //     type: 'success',
            // });
            header.getWebsocketStatus(socket.websock.readyState);
            let data = {
                "action": 10002,
                "token":getToken(),
                "chatMsg": null,
                "extend": 1,
            };
            socket.send(data);
            socket.socket_open = true;
            socket.is_reonnect = true;
            //重修刷新好友内容
            window.dispatchEvent(new CustomEvent('connectInit'));
            // 开启心跳
            socket.heartbeat()
        };
        // 连接发生错误
        socket.websock.onerror = function (err) {
            Message({
                message: '服务连接发送错误!',
                type: 'error',
            });
        }
    },
    /**
     * 获取websocket对象
     * */

    getSocket:()=>{
        //创建了直接返回,反之重来
        if (socket.websock) {
            return socket.websock
        }else {
            socket.init();
        }
    },

    getStatus:()=> {
        if (socket.websock.readyState === 0) {
            return "未连接";
        } else if (socket.websock.readyState === 1) {
            return "已连接";
        } else if (socket.websock.readyState === 2) {
            return "连接正在关闭";
        } else if (socket.websock.readyState === 3) {
            return "连接已关闭";
        }
    },

    /**
     * 发送消息
     * @param {*} data 发送数据
     * @param {*} callback 发送后的自定义回调函数
     */
    send: (data, callback = null) => {
        // 开启状态直接发送
        if (socket.websock!=null && socket.websock.readyState === socket.websock.OPEN) {
            try {
                socket.websock.send(JSON.stringify(data));
            }catch (e) {
                if (socket.timer !=null){
                    return
                }
                socket.timer = setInterval(()=>{
                   if (i>=6){
                       clearInterval(socket.timer);
                       socket.timer = null;
                       socket.i = 0;
                       return
                   }
                  socket.websock.send(JSON.stringify(data));
                    socket.i++;
               },2000)
            }
            if (callback) {
                callback()
            }

            // 正在开启状态,则等待1s后重新调用
        } else if (socket.websock!=null && socket.websock.readyState === socket.websock.CONNECTING) {
            setTimeout(function () {
                socket.send(data, callback)
            }, 1000)

            // 未开启,则等待1s后重新调用
        } else if (socket.websock!=null){
            socket.init();
            setTimeout(function () {
                socket.send(data, callback)
            }, 1000)
        }
    },

    /**
     * 接收消息
     * @param {*} message 接收到的消息
     */
    receive: (message) => {
        var recData = JSON.parse(message.data);
        /**
         *这部分是我们具体的对消息的处理
         * */
           console.log(recData)
        // 自行扩展其他业务处理...
    },

    /**
     * 心跳
     */
    heartbeat: () => {
        if (socket.hearbeat_timer) {
            clearInterval(socket.hearbeat_timer)
        }
        socket.hearbeat_timer = setInterval(() => {
            //发送心跳包
            let data = {
                "action": 10000,
                "token":getToken(),
                "chatMsg": null,
                "extend": null,
            };
            socket.send(data)
        }, socket.hearbeat_interval)
    },

    /**
     * 主动关闭连接
     */
    close: () => {
        if (socket.websock==null){
            return
        }
        let data = {
            "action": 10002,
            "token":getToken(),
            "chatMsg": null,
            "extend": 0,
        };
        socket.send(data);
        clearInterval(socket.hearbeat_interval);
        socket.is_reonnect = false;
        socket.websock.close();
        header.getWebsocketStatus(socket.websock.readyState);
        socket.websock=null
    },

    /**
     * 重新连接
     */
    reconnect: () => {
        if (socket.websock && socket.socket_open) {
            socket.websock.close()
        }
        socket.init()
    },
}

        说明:通过登入后,在某个全局页面中调用socket.start()即可连接netty服务器,通过socket.send("消息")来发送消息。

三、结束语

        以上就是整个的使用教程,如果报错或者有异常,可以在私信我,看到消息第一时间帮你解决,坚持就是胜利,加油!。文章来源地址https://www.toymoban.com/news/detail-808215.html

到了这里,关于SpringBoot整合Netty+Websocket实现消息推送的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • SpringBoot+Netty+Vue+Websocket实现在线推送/聊天系统

    ok,那么今天的话也是带来这个非常常用的一个技术,那就是咱们完成nutty的一个应用,今天的话,我会介绍地很详细,这样的话,拿到这个博文的代码就基本上可以按照自己的想法去构建自己的一个在线应用了。比如聊天,在线消息推送之类的。其实一开始我原来的想法做在

    2024年02月03日
    浏览(39)
  • Spring Boot集成WebSocket实现消息推送

    项目中经常会用到消息推送功能,关于推送技术的实现,我们通常会联想到轮询、comet长连接技术,虽然这些技术能够实现,但是需要反复连接,对于服务资源消耗过大,随着技术的发展,HtML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

    2023年04月08日
    浏览(44)
  • Springboot集成websocket实现消息推送和在线用户统计

    在启动类上添加一个bean 核心代码 实现消息推送只要在业务代码中调用sendMessageSpecial()方法即可。 然后调用刚才的业务接口测试:http://localhost:8080/websocket/t1 调用成功后可以看到三个窗口中都收到了消息

    2023年04月08日
    浏览(53)
  • SpringBoot集成WebSocket实现消息实时推送(提供Gitee源码)

    前言:在最近的工作当中,客户反应需要实时接收消息提醒,这个功能虽然不大,但不过也用到了一些新的技术,于是我这边写一个关于我如何实现这个功能、编写、测试到部署服务器,归纳到这篇博客中进行总结。 目录 一、什么是WebSocket 二、后端实现 2.1、引入pom.xml依赖

    2024年02月11日
    浏览(43)
  • SpringBoot项目整合WebSocket+netty实现前后端双向通信(同时支持前端webSocket和socket协议哦)

    目录   前言 技术栈 功能展示 一、springboot项目添加netty依赖 二、netty服务端 三、netty客户端 四、测试 五、代码仓库地址   专属小彩蛋:前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站(前言 - 床长人工智能教程

    2024年02月12日
    浏览(46)
  • Spring Boot 集成 WebSocket 实现服务端推送消息到客户端

          假设有这样一个场景:服务端的资源经常在更新,客户端需要尽量及时地了解到这些更新发生后展示给用户,如果是 HTTP 1.1,通常会开启 ajax 请求询问服务端是否有更新,通过定时器反复轮询服务端响应的资源是否有更新。                         在长时间不更新

    2024年02月16日
    浏览(55)
  • 【Java】SpringBoot快速整合WebSocket实现客户端服务端相互推送信息

    目录 什么是webSocket? webSocket可以用来做什么? WebSocket操作类 一:测试客户端向服务端推送消息 1.启动SpringBoot项目 2.打开网站 3.进行测试消息推送 4.后端进行查看测试结果 二:测试服务端向客户端推送消息 1.接口代码 2.使用postman进行调用 3.查看测试结果         WebSocke

    2024年01月20日
    浏览(58)
  • Springboot+Netty+WebSocket搭建简单的消息通知

    Springboot+Netty+WebSocket搭建简单的消息通知 一、快速开始 1、添加依赖 2、添加配置 3、添加启动类 二、添加WebSocket部分代码 1、WebSocketServer 2、WebSocketConfig 3、DemoController 6、添加templates/index.html 三、添加Netty部分 1、NettyServer 2、WSChannelHandlerPool 3、WSWebSocketHandler 四、启动服务 ht

    2024年02月11日
    浏览(41)
  • SpringBoot集成WebSocket(实时消息推送)

    🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝 🍓 更多文章请点击 调试工具 :http://coolaf.com/tool/chatt

    2024年04月29日
    浏览(41)
  • SpringBoot整合调用微信模板方法实现微信公众号消息通知推送,Java实现微信公众号给关注用户推送自定义消息通知(手把手从0到1)

    目录 概述 公众号给关注用户推送自定义消息 一、申请公众号模板消息 二、获取安装“web开发者工具” 三、微信网页授权说明 四、微信网页授权 - 流程时序图 五、HTTPClient 实现微信公众号消息推送与发布(四步走) 六、通过weixin-java-mp SDK实现微信公众号消息推送与发布(七

    2024年02月10日
    浏览(41)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包