SpringBoot+Netty+WebSocket實現實時通信


這篇隨筆暫時不講原理,首先搭建起一個簡單的可以實現通信的Demo。之后的一系列隨筆會進行一些原理上的分享。

不過在這之前大家最好了解一下Netty的線程模型和NIO編程模型,會對它的整體邏輯有所了解。

更新一篇關於NIO的博客:手動搭建I/O網絡通信框架3:NIO編程模型,升級改造聊天室

首先創建好項目后在pom.xml引入Netty依賴

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
</dependency>

 

用Netty搭建一個WebSocket服務器整體上需要三樣東西,不管是不是用的SpringBoot框架,這三樣東西是必不可少的。

1.啟動服務器的類(NettyServer),會進行一些初步的配置工作。

2.助手類(Handler),有自己定義的助手類,也有Netty提供的一些基本的助手類,比如對Http、WebSocket支持的助手類。

3.初始化器(Initializer),我們下面使用的是主從線程模型,從線程組里會分配出不同channel去處理不同客戶端的請求,而每個channel里就會有各種助手類去實現一些功能。初始化器的作用就是對各種助手類進行綁定。

 

服務器啟動類:

 

public class NettyServer {
    private static int port;

    public NettyServer(int port) {
        this.port = port;
    }
    public static void start() throws InterruptedException {//在main方法里調用這個方法,並用構造函數設置端口號
        //創建主線程組,接收請求
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //創建從線程組,處理主線程組分配下來的io操作
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        //創建netty服務器
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)//設置主從線程組
                    .channel(NioServerSocketChannel.class)//設置通道
                    .childHandler(new NettyServerInitializer());//子處理器,用於處理workerGroup中的操作
            //啟動server
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            //監聽關閉channel
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();//關閉主線程
            workerGroup.shutdownGracefully();//關閉從線程
        }
    }
}

 

 

初始化器:

 

public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline= socketChannel.pipeline();
        //以下三個是Http的支持
        //http解碼器
        pipeline.addLast(new HttpServerCodec());
        //支持寫大數據流
        pipeline.addLast(new ChunkedWriteHandler());
        //http聚合器
        pipeline.addLast(new HttpObjectAggregator(1024*62));
        //websocket支持,設置路由
        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
        //添加自定義的助手類
        pipeline.addLast(new NettyHandler());
    }
}

 

 

自定義助手類:

這個類就是業務的核心,客戶端的請求會在這里處理。比如客戶端連接、客戶端發送消息、給客戶端發送消息等等。

自定義助手類需要重寫的方法可以根據自己的需求重寫,這里就不把每個方法都重寫一遍了,完整的大家可以去找找文檔看看。

如果需要在助手類中用到@Autowire注解,可以參考這個博客,網上有很多說明,這里就不再重復了https://blog.csdn.net/weixin_30828379/article/details/95009595

 

public class NettyHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {//TextWebSocketFrame是netty用於處理websocket發來的文本對象
  //所有正在連接的channel都會存在這里面,所以也可以間接代表在線的客戶端
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
  //在線人數
    public static int online;
    //接收到客戶都發送的消息
    @Override
    public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        SendAllMessages(ctx,send_message);//send_message是我的自定義類型,前后端分離往往需要統一數據格式,可以先把對象轉成json字符串再發送給客戶端
    }
    //客戶端建立連接
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        channelGroup.add(ctx.channel());
        online=channelGroup.size();
        System.out.println(ctx.channel().remoteAddress()+"上線了!");
    }
    //關閉連接
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        channelGroup.remove(ctx.channel());
        online=channelGroup.size();
        System.out.println(ctx.channel().remoteAddress()+"斷開連接");
    }

    //出現異常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    //給某個人發送消息
    private void SendMessage(ChannelHandlerContext ctx, Send_Message msg) {
        ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(msg)));
    }

    //給每個人發送消息,除發消息人外
    private void SendAllMessages(ChannelHandlerContext ctx,Send_Message msg) {
        for(Channel channel:channelGroup){
            if(!channel.id().asLongText().equals(ctx.channel().id().asLongText())){
                channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(msg)));
            }
        }
    }
}

 

 

 

 

前端創建WebSocket對象進行訪問

通過socket就可以用一些api進行發送消息,接收消息的操作。然后把接收的數據按各自的需求展示出來就行了,前端部分就不再贅述了。

下面8088端口記得要在main方法里面設置。

 

if (window.WebSocket) {
        var host = window.location.hostname;
        var url = "ws://" + host + ":8088/ws";
        var socket = new WebSocket(url);
}else{
     alert("你的瀏覽器不支持WebSocket。請不要使用低版本的IE瀏覽器。");   
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM