最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成netty實(shí)現(xiàn)websocket通信功能

 更新時(shí)間:2024年03月14日 09:11:44   作者:A塵埃  
Netty是一個(gè)高性能、異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用于快速開發(fā)可維護(hù)的高性能協(xié)議服務(wù)器和客戶端,WebSocket 是一種網(wǎng)絡(luò)通信協(xié)議,相比傳統(tǒng)的HTTP協(xié)議,本文給大家介紹了SpringBoot集成netty實(shí)現(xiàn)websocket通信功能,需要的朋友可以參考下

實(shí)現(xiàn)推送消息給指定的用戶

一、依賴

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>netty</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.87.Final</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.1</version>
        </dependency>
    </dependencies>
</project>

二、屬性文件和啟動(dòng)類

server:
  port: 8088

三、Controller接口

@RestController
@RequestMapping("/push")
public class TestController{
	
	@Autowired
	PushMsgService pushMsgService
}

四、PushMsgService

public interface PushMsgService{
	
	//推送給指定用戶
	void pushMsgToOne(String userId,String msg);

	//推送給所有用戶
	void pushMsgToAll(String msg);
}
@Service
public class PushMsgServiceImpl implements PushMsgService{
	
	@Override
	public void pushMsgToOne(String userId, String msg){
		Channel channel = NettyConfig.getChannel(userId);
        if (Objects.isNull(channel)) {
            throw new RuntimeException("未連接socket服務(wù)器");
        }

        channel.writeAndFlush(new TextWebSocketFrame(msg));
	}

	@Override
	public void pushMsgToAll(String msg){
		NettyConfig.getChannelGroup().writeAndFlush(new TextWebSocketFrame(msg));
	}
}

五、NettyConfig

public class NettyConfig{
	
	//定義全局channel,管理所有的channel
	private static volatile ChannelGroup channelGroup = null;

	//存放請(qǐng)求ID與channel的對(duì)應(yīng)關(guān)系
	private static volatile ConcurrentHashMap<String, Channel> channelMap = null;

	//定義兩把鎖
	private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

	public static ChannelGroup getChannelGroup() {
        if (null == channelGroup) {
            synchronized (lock1) {
                if (null == channelGroup) {
                    channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
                }
            }
        }
        return channelGroup;
    }

    public static ConcurrentHashMap<String, Channel> getChannelMap() {
        if (null == channelMap) {
            synchronized (lock2) {
                if (null == channelMap) {
                    channelMap = new ConcurrentHashMap<>();
                }
            }
        }
        return channelMap;
    }

    public static Channel getChannel(String userId) {
        if (null == channelMap) {
            return getChannelMap().get(userId);
        }
        return channelMap.get(userId);
    }
}

六、netty server

@Component
public class NettyServer {
    static final Logger log = LoggerFactory.getLogger(NettyServer.class);

    /**
     * 端口號(hào)
     */
    @Value("${webSocket.netty.port:8889}")
    int port;

    EventLoopGroup bossGroup;
    EventLoopGroup workGroup;

    @Autowired
    ProjectInitializer nettyInitializer;

    @PostConstruct
    public void start() throws InterruptedException {
        new Thread(() -> {
            bossGroup = new NioEventLoopGroup();
            workGroup = new NioEventLoopGroup();
            ServerBootstrap bootstrap = new ServerBootstrap();
            // bossGroup輔助客戶端的tcp連接請(qǐng)求, workGroup負(fù)責(zé)與客戶端之前的讀寫操作
            bootstrap.group(bossGroup, workGroup);
            // 設(shè)置NIO類型的channel
            bootstrap.channel(NioServerSocketChannel.class);
            // 設(shè)置監(jiān)聽端口
            bootstrap.localAddress(new InetSocketAddress(port));
            // 設(shè)置管道
            bootstrap.childHandler(nettyInitializer);

            // 配置完成,開始綁定server,通過調(diào)用sync同步方法阻塞直到綁定成功
            ChannelFuture channelFuture = null;
            try {
                channelFuture = bootstrap.bind().sync();
                log.info("Server started and listen on:{}", channelFuture.channel().localAddress());
                // 對(duì)關(guān)閉通道進(jìn)行監(jiān)聽
                channelFuture.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }

    /**
     * 釋放資源
     */
    @PreDestroy
    public void destroy() throws InterruptedException {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully().sync();
        }
        if (workGroup != null) {
            workGroup.shutdownGracefully().sync();
        }
    }
}

七、ProjectInitializer初始化,設(shè)置websocket handler

@Component
public class ProjectInitializer extends ChannelInitializer<SocketChannel> {

    /**
     * webSocket協(xié)議名
     */
    static final String WEBSOCKET_PROTOCOL = "WebSocket";

    /**
     * webSocket路徑
     */
    @Value("${webSocket.netty.path:/webSocket}")
    String webSocketPath;
    @Autowired
    WebSocketHandler webSocketHandler;

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        // 設(shè)置管道
        ChannelPipeline pipeline = socketChannel.pipeline();
        // 流水線管理通道中的處理程序(Handler),用來處理業(yè)務(wù)
        // webSocket協(xié)議本身是基于http協(xié)議的,所以這邊也要使用http編解碼器
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ObjectEncoder());
        // 以塊的方式來寫的處理器
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpObjectAggregator(8192));
        pipeline.addLast(new WebSocketServerProtocolHandler(webSocketPath, WEBSOCKET_PROTOCOL, true, 65536 * 10));
        // 自定義的handler,處理業(yè)務(wù)邏輯
        pipeline.addLast(webSocketHandler);
    }
}

八、WebSocketHandler

@Component
@ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private static final Logger log = LoggerFactory.getLogger(NettyServer.class);

    /**
     * 一旦連接,第一個(gè)被執(zhí)行
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("有新的客戶端鏈接:[{}]", ctx.channel().id().asLongText());
        // 添加到channelGroup 通道組
        NettyConfig.getChannelGroup().add(ctx.channel());
    }

    /**
     * 讀取數(shù)據(jù)
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        log.info("服務(wù)器收到消息:{}", msg.text());

        // 獲取用戶ID,關(guān)聯(lián)channel
        JSONObject jsonObject = JSONUtil.parseObj(msg.text());
        String uid = jsonObject.getStr("uid");
        NettyConfig.getChannelMap().put(uid, ctx.channel());

        // 將用戶ID作為自定義屬性加入到channel中,方便隨時(shí)channel中獲取用戶ID
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        ctx.channel().attr(key).setIfAbsent(uid);

        // 回復(fù)消息
        ctx.channel().writeAndFlush(new TextWebSocketFrame("服務(wù)器收到消息啦"));
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("用戶下線了:{}", ctx.channel().id().asLongText());
        // 刪除通道
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("異常:{}", cause.getMessage());
        // 刪除通道
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();
    }

    /**
     * 刪除用戶與channel的對(duì)應(yīng)關(guān)系
     */
    private void removeUserId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = ctx.channel().attr(key).get();
        NettyConfig.getChannelMap().remove(userId);
    }
}

測(cè)試:

postman創(chuàng)建websocket連接 ws://127.0.0.1:8889/webSocket,并發(fā)送消息{‘uid’:‘sss’}給服務(wù)端

在這里插入圖片描述

打開瀏覽器,給用戶sss推送消息 http://127.0.0.1:8088/push/sss

在這里插入圖片描述

以上就是SpringBoot集成netty實(shí)現(xiàn)websocket通信功能的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot netty websocket通信的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaWeb文件上傳流程

    JavaWeb文件上傳流程

    這篇文章主要介紹了JavaWeb文件上傳流程,JavaWeb中最重要的技術(shù)之一,下文關(guān)于其文件上傳的流程分享,需要的小伙伴可以參考一下
    2022-05-05
  • SpringBoot工程Docker多環(huán)境中使用同一個(gè)Jar包解決方案

    SpringBoot工程Docker多環(huán)境中使用同一個(gè)Jar包解決方案

    在Docker多環(huán)境部署中,SpringBoot工程可以通過環(huán)境變量來動(dòng)態(tài)改變配置,無需重新打包,利用volume掛載或docker?cp命令,可以將配置文件直接傳入容器,提高部署效率,并保證安全性
    2024-09-09
  • Java IO之包裝流詳解

    Java IO之包裝流詳解

    這篇文章主要為大家介紹了Java IO之包裝流,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 詳解Java中switch的新特性

    詳解Java中switch的新特性

    這篇文章主要介紹了Java中switch的新特性,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • RabbitMQ?延時(shí)隊(duì)列插件安裝與使用示例詳解(基于?Delayed?Message?Plugin)

    RabbitMQ?延時(shí)隊(duì)列插件安裝與使用示例詳解(基于?Delayed?Message?Plugin)

    本文詳解RabbitMQ通過安裝rabbitmq_delayed_message_exchange插件實(shí)現(xiàn)消息級(jí)延遲投遞,需配置交換機(jī)類型和header設(shè)置延遲時(shí)間,相比原生TTL方案更靈活簡(jiǎn)潔,適用于訂單超時(shí)、注冊(cè)郵件等場(chǎng)景,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2025-08-08
  • 兩萬字詳解Java Sring String的常見操作以及StringBuffer StringBuilder的區(qū)別

    兩萬字詳解Java Sring String的常見操作以及StringBuffer StringBuilder的區(qū)別

    本篇文章帶你認(rèn)識(shí)Sring、String的常見操作和StringBuffer 與StringBuilder的區(qū)別(字符串詳解),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Maven依賴管理中<o(jì)ptional>與<scope>標(biāo)簽的用法詳解

    Maven依賴管理中<o(jì)ptional>與<scope>標(biāo)簽的用法詳解

    Maven 作為 Java 項(xiàng)目的核心構(gòu)建工具,其依賴管理機(jī)制是項(xiàng)目開發(fā)的基石,在復(fù)雜的項(xiàng)目中,合理管理依賴不僅能提升構(gòu)建效率,還能有效避免版本沖突和冗余依賴,本文給大家介紹了Maven依賴管理中<o(jì)ptional>與<scope>標(biāo)簽的用法,需要的朋友可以參考下
    2025-07-07
  • Mybatis逆向生成使用擴(kuò)展類的實(shí)例代碼詳解

    Mybatis逆向生成使用擴(kuò)展類的實(shí)例代碼詳解

    這篇文章主要介紹了Mybatis逆向生成使用擴(kuò)展類的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-05-05
  • Spring框架讀取property屬性文件常用5種方法

    Spring框架讀取property屬性文件常用5種方法

    這篇文章主要介紹了Spring框架讀取property屬性文件常用5種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany

    使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany

    這篇文章主要介紹了使用JPA雙向多對(duì)多關(guān)聯(lián)關(guān)系@ManyToMany,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評(píng)論

渝北区| 远安县| 土默特右旗| 渑池县| 眉山市| 贵阳市| 松滋市| 陇川县| 灵川县| 平南县| 铜鼓县| 江安县| 新田县| 丹江口市| 成武县| 天等县| 韶山市| 自治县| 宜昌市| 滦平县| 临澧县| 方山县| 琼结县| 穆棱市| 许昌市| 孟连| 南漳县| 双鸭山市| 类乌齐县| 东明县| 房山区| 平南县| 图木舒克市| 习水县| 页游| 丹江口市| 彰化市| 奉新县| 从化市| 丁青县| 桃园市|