Netty TCP服务器
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()
方法。在这里,我们可以决定发生什么情况,例如关闭连接或者使用错误代码进行响应等。