下面用于分析源码的例子摘自《Netty权威指南 第二版》

本源码版本为5.0.0.Alpha1

server

package com.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeServer {
	
	
	public void bind(int port) {
		//NIO线程组,Reactor线程组,一个用于接受客户端的请求,另一个用于进行SocketChannel的操作
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		
		try {
			//用于NIO服务端启动的辅助类
			ServerBootstrap b = new ServerBootstrap();
			
			b.group(bossGroup, workerGroup)
			  .channel(NioServerSocketChannel.class)
			  .option(ChannelOption.SO_BACKLOG, 1024)
			  .childHandler(new ChildChannelHandler());
			
			//绑定端口,同步等待成功,一直等待到绑定端口成功,返回一个ChannelFuture,类似JDK中的java.util.concurrent.Future
			//用于异步的通知回调
			ChannelFuture f = b.bind(port).sync();
			
			//进行阻塞,等待服务器链路关闭,就退出
			f.channel().closeFuture().sync();
		
		
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			
			//优雅退出,释放线程资源
			
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
			
		}
		
	}

	private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

		@Override
		protected void initChannel(SocketChannel arg0) throws Exception {
			arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));//加入换行符解码器
			arg0.pipeline().addLast(new StringDecoder());//加入字符串解码器,将会把msg直接变成String类型的信息
			arg0.pipeline().addLast(new TimeServerHandler());
			
		}
		
		
	}
	
	
	public static void main(String[] args) {
		
		int port = 8080;
		
		if(args != null && args.length > 0) {
			try {
				
				port = Integer.valueOf(args[0]);
				
			} catch (NumberFormatException e) {
				
				
			}
			
		}
		
		new TimeServer().bind(port);
		
	}
	

}
package com.netty.server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeServerHandler extends ChannelHandlerAdapter {
	
	private int counter;

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//因为在channel中添加了StringDecoder解码器,所以收取到值是String类型的
		String body = (String) msg;
		
		System.out.println("The time server receive order : " + body + " ; the counter is : " + ++counter);
		
		String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";
		
		currentTime += System.lineSeparator();
		
		ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
		//先写到缓存中,需要flush
		ctx.write(resp);
		
		
		
		
	}
	
	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		//刷新
		ctx.flush();
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
	

}

client

package com.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;

public class TimeClient {

	public void connect(int port, String host) throws Exception {

		// 配合客户端NIO线程组
		EventLoopGroup group = new NioEventLoopGroup();

		try {
			//客户端启动类
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() {

						@Override
						protected void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new LineBasedFrameDecoder(1024));//加入换行符解码器
							ch.pipeline().addLast(new StringDecoder());//加入字符串解码器
							ch.pipeline().addLast(new TimeClientHandler());
						}
					});
			
			//发起异步连接操作sync()表示在链接成功之前一直等待
			ChannelFuture f = b.connect(host, port).sync();
			
			//等待客户端链路关闭,sync()在关闭之前一直阻塞
			f.channel().closeFuture().sync();
			
			
			
		} catch (Exception e) {

		} finally {
			//优雅的关闭,释放资源
			group.shutdownGracefully();
			
		}

	}
	
	public static void main(String[] args) throws Exception {
		
		int port = 8080;
		
		if(args != null &&  args.length > 0) {
			try {
				port = Integer.valueOf(args[0]);
				
			} catch(NumberFormatException e) {
				
				//采用默认值
			}
		}
		
		new TimeClient().connect(port, "localhost");
		
		
		
		
		
		
	}
	
	
}
package com.netty.client;

import java.util.logging.Logger;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class TimeClientHandler extends ChannelHandlerAdapter {

	private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());
	
	private byte[] req = null;
	
	private int counter;
	
	public TimeClientHandler() {
		
		req = ("QUERY TIME ORDER" + System.lineSeparator()).getBytes();
		
	}
	
	//通道准备就绪,就直接发送信息到服务器
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		ByteBuf message = null;
		
		//向服务端发送一百条消息,由于存在tcp的粘包,拆包的问题,服务段可能会显示收取到的信息没有一百条
		for(int i = 0; i < 100; i++) {
			message = Unpooled.buffer(req.length);
			message.writeBytes(req);
			ctx.writeAndFlush(message);
		}
		
		
		
	}
	
	/**
	 * 当服务器发送信息回来时会调用这个方法
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//因为在channel中添加了StringDecoder解码器,所以收取到值是String类型的
		String body = (String) msg;
		System.out.println("Now is : " + body + " ; the counter is : " + ++counter);
		
		
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		//释放资源
		logger.warning("Unexpected exception from dodwnstream : " + cause.getMessage());
		
		ctx.close();
	}
	
}