SpringBoot+Netty+WebSocket實現實時通訊

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

不過在這以前你們最好了解一下Netty的線程模型和NIO編程模型,會對它的總體邏輯有所瞭解。前端

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

首先建立好項目後在pom.xml引入Netty依賴編程

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

 

用Netty搭建一個WebSocket服務器總體上須要三樣東西,無論是否是用的SpringBoot框架,這三樣東西是必不可少的。json

1.啓動服務器的類(NettyServer),會進行一些初步的配置工做。後端

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

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();//關閉從線程
        }
    }
}

 

 

初始化器:websocket

 

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瀏覽器。");   
}
相關文章
相關標籤/搜索