SpringBoot中使用Netty實現TCP通訊,服務器主動向客戶端發送數據

簡述:

Springboot項目的web服務後臺,web服務運行在9100端口。

後臺使用netty實現了TCP服務,運行在8000端口。

啓動截圖如下:

 

  

pom依賴

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


netty服務代碼

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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * netty服務器,主要用於與客戶端通訊
 */
public class NettyServer {
    private final int port; //監聽端口


    public NettyServer(int port) {
        this.port = port;
    }

    //編寫run方法,處理客戶端的請求
    public void run() throws Exception {

        //創建兩個線程組
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //8個NioEventLoop

        try {
            ServerBootstrap b = new ServerBootstrap();

            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //獲取到pipeline
                            ChannelPipeline pipeline = ch.pipeline();
                            //向pipeline加入解碼器
                            pipeline.addLast("decoder", new StringDecoder());
                            //向pipeline加入編碼器
                            pipeline.addLast("encoder", new StringEncoder());
                            //加入自己的業務處理handler
                            pipeline.addLast(new NettyServerHandler());

                        }
                    });
            System.out.println("netty服務器啓動成功(port:" + port + ")......");
            ChannelFuture channelFuture = b.bind(port).sync();
            //監聽關閉
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}

netty業務處理handler

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {

    //定義一個channle 組,管理所有的channel
    //GlobalEventExecutor.INSTANCE) 是全局的事件執行器,是一個單例
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 有客戶端與服務器發生連接時執行此方法
     * 1.打印提示信息
     * 2.將客戶端保存到 channelGroup 中
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.err.println("有新的客戶端與服務器發生連接。客戶端地址:" + channel.remoteAddress());
        channelGroup.add(channel);
    }

    /**
     * 當有客戶端與服務器斷開連接時執行此方法,此時會自動將此客戶端從 channelGroup 中移除
     * 1.打印提示信息
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        System.err.println("有客戶端與服務器斷開連接。客戶端地址:" + channel.remoteAddress());
    }

    /**
     * 表示channel 處於活動狀態
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + " 處於活動狀態");
    }

    /**
     * 表示channel 處於不活動狀態
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + " 處於不活動狀態");
    }

    /**
     * 讀取到客戶端發來的數據數據
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        //獲取到當前channel
        Channel channel = ctx.channel();
        System.err.println("有客戶端發來的數據。地址:" + channel.remoteAddress() + " 內容:" + msg);
    }

    /**
     * 處理異常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("發生異常。異常信息:{}", cause.getMessage());
        //關閉通道
        ctx.close();
    }
}

Springboot項目中啓動netty服務

啓動類修改:

測試結果截圖

服務啓動,及客戶端連接

客戶端給服務器發送數據

服務器給客戶端發送數據

服務器查看當前所有連接的客戶端

服務器獲取到所有客戶單的ip地址及端口號後,即可通過其給指定客戶端發送數據

 

 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章