第一个netty程序——编写Echo服务器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yikong2yuxuan/article/details/80556461

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

①至少一个ChannelHandler——该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑。

②引导——这是配置服务器的启动代码。至少,它会将服务器绑定到连接请求的端口上。

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

package netty.server;

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 //标示一个Channel-Handler可以被多个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();
	}
}

ChannelInboundHandlerAdapter有一个直观的API,并且它的每个方法都可以被重写以挂钩到事件生命周期的恰当点上。因为需要处理所有接收到的数据,所以重写了channelRead()方法。在这个服务器应用程序中,你将数据简单地送给了远程节点。

重写exceptionCaught()允许你对Throwable的任何子类型做出反应。

如果不捕获异常,会发生什么呢?

每个channel都拥有一个与之相关联的ChannelPipeline,其持有一个ChannelHandler的实例链。在默认情况下,ChannelHandler会把对它的方法的调用转发给链接中的下一个ChannelHandler。因此,如果exceptionCaught()方法没有被该链中的某处实现,那么所接收的异常将会被传递到ChannelPipeline的尾端并被记录。为此,你的应用该程序应该提供至少有一个实现了exceptionCaught()方法的ChannelHandler。

引导服务器

①绑定到服务器将在其上监听并接受传入连接请求的端口;

②配置channel,并将有关的入站消息通知给EchoServerHandler实例。

package netty.server;

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()+"<port>");
			return;
		}
		//设置端口值(如果端口参数的格式不正确,则抛出一个NumberFormatException)
		int port = Integer.parseInt(args[0]);
		//调用服务器的start()方法
		new EchoServer(port).start();
	}
	
	public void start() throws Exception {
		final EchoServerHandler serverHandler = new EchoServerHandler();
		//创建Event-LoopGroup
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			//创建ServerBootstrap
			ServerBootstrap b = new ServerBootstrap();
			b.group(group)
				//指定所使用的NIO传输Channel
				.channel(NioServerSocketChannel.class)
				//使用制定的端口设置套接字地址
				.localAddress(new InetSocketAddress(port))
				//添加一个EchoServer-Handler到子Channel的ChannelPipeline
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						//EchoServerHandler被标注为@Shareable,所以我们可以总是使用同样的实例
						ch.pipeline().addLast(serverHandler);
					}
				});
			//异步的绑定服务器;调用sync()方法阻塞等待直到绑定完成
			ChannelFuture f = b.bind().sync();
			//获取Channel的CloseFuture,并且阻塞当前线程直到它完成
			f.channel().closeFuture().sync();
		}finally {
			//关闭EventLoopGroup,释放所有的资源
			group.shutdownGracefully().sync();
		}
	}
}

猜你喜欢

转载自blog.csdn.net/yikong2yuxuan/article/details/80556461