Netty TCP服务器

时间:2020-01-09 10:36:53  来源:igfitidea点击:

Netty的服务器之一是TCP服务器。要创建Netty TCP服务器,我们必须:

  • 创建一个EventLoopGroup
  • 创建和配置ServerBootstrap
  • 创建一个ChannelInitializer
  • 启动服务器

这是一个代码示例,显示了如何创建Netty TCP服务器:

EventLoopGroup group = new NioEventLoopGroup();

try{
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(group);
    serverBootstrap.channel(NioServerSocketChannel.class);
    serverBootstrap.localAddress(new InetSocketAddress("localhost", 9999));

    serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
        protected void initChannel(SocketChannel socketChannel) throws Exception {
            socketChannel.pipeline().addLast(new HelloServerHandler());
        }
    });
    ChannelFuture channelFuture = serverBootstrap.bind().sync();
    channelFuture.channel().closeFuture().sync();
} catch(Exception e){
    e.printStackTrace();
} finally {
    group.shutdownGracefully().sync();
}

该过程分为较小的步骤,并在以下各节中分别进行描述。

创建一个EventLoopGroup

创建Netty TCP服务器的第一步是创建一个NettyEventLoopGroup。由于此示例使用Java NIO,因此创建了" NioEventLoopGroup"。这是创建EventLoopGroup的行:

EventLoopGroup group = new NioEventLoopGroup();

创建一个ServerBootStrap

创建Netty TCP服务器的下一步是创建和配置ServerBootStrap。可以通过以下几行完成此操作:

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.localAddress(new InetSocketAddress("localhost", 9999));

首先,创建一个ServerBootstrap实例。

其次,在" ServerBootstrap"实例上设置" EventLoopGroup"。

第三,在" ServerBootstrap"实例上设置" NioServerSocketChannel"类实例。这是必需的,因为该示例使用了NioEventLoopGroup

第四,在" ServerBootstrap"上设置本地IP地址/域+ TCP端口。要启动Netty TCP服务器,这是必需的。

创建一个ChannelInitializer

启动Netty TCP服务器的第三步是创建一个ChannelInitializer并将其添加到ServerBootstrap实例。 ChannelInitializer初始化所有传入TCP连接的套接字。

在以下几行中完成创建和添加ChannelInitializer的操作:

serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast(new HelloServerHandler());
    }
});

NettyChannelInitializer类是一个抽象类。每当TCP服务器接受新的传入TCP连接时,就会调用其方法" initChannel()"。如我们所见,它将一个新的HelloServerHandler(一个ChannelHandler)添加到每个新的SocketChannel。也有可能在所有新的" SocketChannel"实例中重用相同的" ChannelHandler",而不是每次都在此处创建新的实例。

如我们所见,通过使用childHandler()方法将ChannelInitializer添加到ServerBootstrap中。之所以称为"子"处理程序,是因为每个接受的" SocketChannel"都被视为接受它的服务器套接字的"子"。

启动服务器

引导Netty TCP服务器的最后一步是启动服务器。使用以下行来启动TCP服务器:

ChannelFuture channelFuture = serverBootstrap.bind().sync();

serverBootstrap.bind()方法返回一个ChannelFuture,它可以用来知道何时完成服务器的绑定(绑定到本地地址和TCP端口)。通过在ChannelFuture上调用sync(),创建服务器的主线程将等到服务器启动后再继续。顺便说一下,sync()方法也返回ChannelFuture

HelloServerHandler

由ChannelInitializer添加到每个SocketChannel的HelloServerHandler是处理来自客户端连接的传入数据的组件。以下是" HelloServerHandler"代码的外观:

public class HelloServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf inBuffer = (ByteBuf) msg;

        String received = inBuffer.toString(CharsetUtil.UTF_8);
        System.out.println("Server received: " + received);

        ctx.write(Unpooled.copiedBuffer("Hello " + received, CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

每当从连接有HelloServerHandler实例的SocketChannel接收数据时,都会调用channelRead()方法。如我们所见,channelRead()会以" Hello" +客户端发送到服务器的任何内容作为响应。

当没有更多数据可从" SocketChannel"中读取时,将调用" channelReadComplete()"方法。

如果从SocketChannel接收或者发送数据时抛出异常,则调用exceptionCaught()方法。在这里,我们可以决定发生什么情况,例如关闭连接或者使用错误代码进行响应等。