Netty编写Echo服务器(一)

所有的Netty服务器都需要以下两个部分

  • 至少一个ChannelHandler——该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑
  • 引导——这是配置服务器的启动代码。至少,它会将服务器绑定到它要监听连接请求的端口上

ChannelHandler和业务逻辑

由于Echo服务器会响应传入的消息,因此只需要实现ChannelnboundHandler接口,用来定义响应入站的事件方法。这个简单的应用程序只需要用到少量的这些方法,因而只需要继承ChannelInboundHandlerAdapter类就足够了,它提供了ChannelInboundHandler的默认实现

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

@Sharable		//标示一个ChannelHandler可以被多个Channel安全的共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf in = (ByteBuf) msg;
		//将消息记录到控制台
		System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
		ctx.write(in);	//将接收到的消息写给发送者,而不冲刷出站消息
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		//将为决消息冲刷到远程节点,并且关闭该Channel
		ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		cause.printStackTrace();		//打印异常栈跟踪
		ctx.close();					//关闭该Channel
	}

}

引导服务器

上面已经写了EchoServerHandler实现的核心业务逻辑之后,接下来开始引导服务器的编写了,它主要涉及一下内容

  • 绑定到服务器将在其上监听并接受传入连接请求的端口
  • 配置Channel,以将有关的入站消息通知给EchoServerChannel实例
import java.net.InetSocketAddress;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class EchoServer {

	private final int port;
	
	public EchoServer(int port) {
		this.port = port;
	}
	
	public static void main(String[] args) throws Exception{
		if(args.length != 1) {
			System.err.println("Usage: " + EchoServer.class.getSimpleName() + " ");
		}
		//设置端口值(如果端口参数的格式不正确,则抛出一个NumberFormatException)
		int port = Integer.parseInt(args[0]);
		new EchoServer(port).start();		//调用服务器的start方法
	}
	
	public void start() throws Exception {
		final EchoServerHandler serverHandler = new EchoServerHandler();
		EventLoopGroup group = new NioEventLoopGroup();					//1、创建EventLoopGroup
		try {
			ServerBootstrap b = new ServerBootstrap();					//2、常见ServerBootstrap
			b.group(group)
			.channel(NioServerSocketChannel.class)						//3、指定所使用的NIO传输Channel
			.localAddress(new InetSocketAddress(port))					//4、使用指定的端口设置套接字地址
			.childHandler(new ChannelInitializer() {		//5、添加一个EchoServerHandler到子Channel的ChannelPipeline

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(serverHandler);				//EchoServerHandler被标注为@Shareable,所以我们可以总是使用同样的实例
					
				}
			});
			ChannelFuture f= b.bind().sync();							//6、异步绑定服务器,调用sync()方法阻塞等待直到绑定完成
			f.channel().closeFuture().sync();							//7、获取Channel的CloseFuture,并且阻塞当前线程直到它完成
		} catch (Exception e) {
			group.shutdownGracefully().sync();							//8、关闭EventLoopGroup释放所有的资源
		}
	}
}

       在2处,创建了一个ServerBootstrap实例,因为目前正在使用NIO传输,所以指定了NioEventLoopGroup(1)来接收和处理新的连接,并且将Channel的类型指定为NioServerSocketChannel(3),在此之后,将本地地址设置为一个具有选定端口的InetSocketAddress(4),服务器将绑定到这个地址以监听新的连接请求

        在5处,使用了一个特殊的类——ChannelInitializer。这是关键,当一个新的连接被接受是,一个新的子Channel将会被创建,而ChannelInitialize将会把一个你的EchoServerHandler的实例添加到该Channel的ChannelPipeline中。之前所声明的ChannelHandler将会收到有关入站的消息通知

         接下来绑定了服务器6,并等待绑定完成。(对sync()方法的调用将导致当前Thread阻塞,一直到绑定操作完成为止)。在7处,该应用程序将会阻塞等待直到服务器的Channel关闭(因为在Channel的CloseFuture上调用了sync()方法)。然后,你将可以关闭EventLoopGroup,并释放所有的资源,包括所有被创建的线程8

创建服务器的步骤的主要代码组件如下:

  • EchoServerHandler实现了业务逻辑;
  • main()方法引导了服务器

引导过程如下:

  • 创建一个ServerBootstrap的实例以引导和绑定服务器;
  • 创建并分配一个NioEventLoopGroup实例以进行实践的处理,如接受新的连接以及读/写数据;
  • 指定服务器绑定的本地的InetSocketAddress;
  • 使用一个EchoServerHandler的实例初始化每一个新的Channel;
  • 调用ServerBootstrap.bind()方法以绑定服务器

你可能感兴趣的:(java)