Netty官方例子(TIME协议)

TIME协议实现

一.TIME协议

TIME协议,此协议提供了一个独立于站点的,机器可读的日期和时间信息。时间服务返回的是以秒数,是从1900年1月1日午夜到现在的秒数,具体的中文解释可以看百度百科,时间协议。

二.服务端

(1).TimeServerHandler

package io.netty.example.time;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @作者:CJY
 * @说明:用来实现Time协议的
 * @时间:2017-4-8下午2:00:19
 */
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
	/* 
	 * @作者:CJY
	 * @说明:channelActive():当一个连接被建立并且准备产生流量(traffic)的时候
	 * @时间:2017-4-8下午2:00:42
	 * @see io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.ChannelHandlerContext)
	 * @param ctx
	 * @throws Exception
	 */
	@Override
	public void channelActive(final ChannelHandlerContext ctx) throws Exception {
		//为了发送消息,我们需要产生一个新的buffer用于放消息
		//因为例子当中要放一个32位的Integer,因此需要一个ByteBuf(容量至少4bytes)
		//通过ctx.alloc()获得ByteBufAllocator接口的实现,ByteBufAllocator用于分配产生buffers
		//为啥ByteBuf不用调用Nio当中的flip()方法?因为它不包含flip()方法而是将他分为两点:一个负责读操作一个负责写操作,
		//负责写操作的当ByteBuf写入的时候它的索引就会增加,而负责读操作的索引不会有变化
		//负责读和负责写它们各自的索引代表了消息开始和结束的地方,Nio没有这种明确的方式,只能使用flip,这就会造成你忘记调用flip时,
		//发送错误的数据或者发送空消息这一问题
		final ByteBuf time=ctx.alloc().buffer(4);
		time.writeInt((int)(System.currentTimeMillis()/1000L+2208988800L));
		//ChannelFuture代表一个还未发生的I/O操作
		//ChannelFuture意味着任何请求操作可能未被执行,因为Netty是异步的
		final ChannelFuture f=ctx.writeAndFlush(time);
		//当调用write()方法返回ChannelFuture并且已经是完成状态时,你需要调用close方法
		//那么我们怎么知道请求已经完成了呢?最简单的方式就是为ChannelFuture添加一个ChannelFutureListener监听
		//它会告诉我们这个ChannelFuture是否已经完成
		f.addListener(new ChannelFutureListener() {
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				assert f == future;
				ctx.close();
			}
		});
		//如果上面你嫌麻烦,可以使用Netty的实现
		//f.addListener(ChannelFutureListener.CLOSE);
	}
	
	@Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

(2).TimeServer

package io.netty.example.time;

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;

public class TimeServer {
	private int port;
	public TimeServer(int port){
		this.port = port;
	}
	
	public void run() throws Exception{
		//NioEventLoopGroup是一个处理I/O操作的多线程事件循环
		//bossGroup作为boss,接收传入连接
		//bossGroup只负责接收客户端连接,不做复杂的处理,为了减少资源占用,取值越小越好
		//Group:群组,Loop:循环,Event:事件,这几个东西联在一起,相比大家也大概明白它的用途了。
		//Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期。
		EventLoopGroup bossGroup=new NioEventLoopGroup(1);
		//workerGroup作为worker,处理boss接收的连接的流量和将接收的连接注册进入这个worker
		EventLoopGroup workerGroup=new NioEventLoopGroup();
		try {
			//ServerBootstrap负责建立服务端
			//你可以直接使用Channel去建立服务端,但是大多数情况下你无需做这种乏味的事情
			ServerBootstrap b=new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			//指定使用NioServerSocketChannel产生一个Channel用来接收连接
			.channel(NioServerSocketChannel.class)
			//ChannelInitializer用于配置一个新的Channel
			//用于向你的Channel当中添加ChannelInboundHandler的实现
			.childHandler(new ChannelInitializer() {
				public void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new TimeServerHandler());
				};
			})
			//对Channel进行一些配置
			//注意以下是socket的标准参数
			//BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。
			//Option是为了NioServerSocketChannel设置的,用来接收传入连接的
			.option(ChannelOption.SO_BACKLOG, 128)
			//是否启用心跳保活机制。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。
			//childOption是用来给父级ServerChannel之下的Channels设置参数的
			.childOption(ChannelOption.SO_KEEPALIVE, true);
			// Bind and start to accept incoming connections.
			ChannelFuture f=b.bind(port).sync();
			// Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
			//sync()会同步等待连接操作结果,用户线程将在此wait(),直到连接操作完成之后,线程被notify(),用户代码继续执行
			//closeFuture()当Channel关闭时返回一个ChannelFuture,用于链路检测
            f.channel().closeFuture().sync();
		}finally{
			//资源优雅释放
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
	
	public static void main(String[] args) {
		int port=8088;
		try {
			new TimeServer(port).run();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


三.客户端

(1).TimeClientHandler

package io.netty.example.time;

import java.util.Date;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeClientHandler extends ChannelInboundHandlerAdapter {
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		ByteBuf m=(ByteBuf)msg;
		try {
			//m.readUnsignedInt获取ByteBuf当中无符号的32-bit的integer
			//就是我们writeInt的那个时间
			long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
            System.out.println(new Date(currentTimeMillis));
            ctx.close();
		} finally {
			m.release();
		}
	}
}

(2).TimeClient

package io.netty.example.time;


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.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
//为啥Time协议需要一个客户端
//官方说的是因为人类无法将一个32bit的二进制数据转换成日期
public class TimeClient {
	public static void main(String[] args) throws Exception {
		//Group:群组,Loop:循环,Event:事件,这几个东西联在一起,相比大家也大概明白它的用途了。
		//Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期。
		//如果你只使用了一个EventLoopGroup,它将既被用于boss group又被用于worker group
		//boss group是不被用于客户端的
		EventLoopGroup workerGroup=new NioEventLoopGroup();
		try {
			//Bootstrap客户端用于简单建立Channel
			//childOption不能用于Bootstrap
			Bootstrap b=new Bootstrap();
			b.group(workerGroup);
			//NioSocketChannel用于客户端创建Channel
			b.channel(NioSocketChannel.class);
			b.option(ChannelOption.SO_KEEPALIVE,true);
			b.handler(new ChannelInitializer() {
				@Override
				public void initChannel(SocketChannel ch) throws Exception {
					//指定使用的数据处理方式
					ch.pipeline().addLast(new TimeClientHandler());
				};
			});
			//客户端开始连接
			ChannelFuture f=b.connect("localhost",8088).sync();
			//等待直到这个连接被关闭
			f.channel().closeFuture().sync();
		} finally {
			workerGroup.shutdownGracefully();
		}
	}
}


你可能感兴趣的:(Netty)