一個簡單的netty通訊的例子

陈鸿圳發表於2024-05-27

新增依賴

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

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.12</version>
</dependency>

服務端

【NettyServerTest.java】

@Slf4j
public class NettyServerTest
{
    public static void main(String[] args) throws Exception
    {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 建立伺服器端的啟動物件,配置引數
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new MyServerChannelInitializer());

            // 繫結埠,同步等待成功
            ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();

            // 等待服務端監聽埠關閉
            channelFuture.channel().closeFuture().sync();
            log.info("end!!!!!!!!!!!!!!!!!!!!!!");
        } finally {
            // 優雅退出,釋放執行緒池資源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

【MyServerChannelInitializer.java】

public class MyServerChannelInitializer extends ChannelInitializer<SocketChannel>
{
    @Override
    public void initChannel(SocketChannel socketChannel) {
        socketChannel.pipeline().addLast(new StringDecoder());
        socketChannel.pipeline().addLast(new StringEncoder());
        socketChannel.pipeline().addLast(new MyServerChannelInBoundHandler());
    }
}

這裡要注意pipeline裡面的Handler的順序,【StringEncoder】一定要放到【MyServerChannelInBoundHandler】的前面,因為【MyServerChannelInBoundHandler】是傳送字串資料出去,字型串要經過【StringEncoder】編碼成位元組碼之後才能成功傳送出去。
【MyServerChannelInBoundHandler.java】

@Slf4j
public class MyServerChannelInBoundHandler extends SimpleChannelInboundHandler<String>
{
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("MyChannelInBoundHandler.channelActive[" + ctx.channel().remoteAddress() + "]: ");
        ctx.writeAndFlush("I'm server, thanks for connected!\r\n");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("MyChannelInBoundHandler.channelReadComplete[" + ctx.channel().remoteAddress() + "]");
        super.channelReadComplete(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        log.info("MyChannelInBoundHandler.channelRead0[" + ctx.channel().remoteAddress() + "]:" + msg);
        ctx.writeAndFlush(msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("MyChannelInBoundHandler.channelInactive[" + ctx.channel().remoteAddress() + "]: ");
    }
}

客戶端

【NettyClientTest.java】

@Slf4j
public class NettyClientTest
{
    public static void main(String[] args) throws Exception
    {
        EventLoopGroup workerEventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(workerEventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new MyClientChannelInitializer());
            ChannelFuture future = bootstrap.connect("localhost", 8080).sync();
            future.channel().closeFuture().sync();
        } finally {
            // 6. 關閉工作執行緒組
            workerEventLoopGroup.shutdownGracefully();
        }
    }
}

【MyClientChannelInitializer.java】

public class MyClientChannelInitializer extends ChannelInitializer<SocketChannel>
{
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast(new StringDecoder()); // 新增字串解碼器
        socketChannel.pipeline().addLast(new StringEncoder()); // 新增字串編碼器
        socketChannel.pipeline().addLast(new MyClientInBoundHandler()); // 自定義的客戶端處理器
    }
}

注意【StringEncoder】和【MyClientInBoundHandler】的順序要正確
【MyClientInBoundHandler.java】

@Slf4j
public class MyClientInBoundHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("channelActive[" + ctx.channel().remoteAddress() + "]");
        ctx.writeAndFlush("Hello, Server, I'm client!\r\n");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("channelReadComplete[" + ctx.channel().remoteAddress() + "]: " );
        super.channelReadComplete(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("channelRead[" + ctx.channel().remoteAddress() + "]: " + msg);
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("channelInactive[" + ctx.channel().remoteAddress() + "]");
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("exceptionCaught", cause);
        ctx.close();
    }
}

相關文章