Netty入门应用一

      在本例子中使用的是netty5,以及后面的例子中也是。先去官网进下载5.0.0.Alphal,解压后要引用的jar包就是netty-5.0.0.Alphal.jar包。对于用netty进行编写TimeServer的nio程序如下:

package zou;

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 static void main(String[] args) throws Exception {
		int port = 8080;
		if (args != null && args.length > 0) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (Exception e) {

			}
		}
		new TimeServer().bind(port);
	}

	public void bind(int port) throws Exception {
		//配置服务端的nio线程组,
		//EventLoopGroup是一个线程组,它包含了一组nio线程,专门用于网络事件的处理,实际上他们就是Reactor线程组
		//这里创建2个的原因是一个用于服务端接受客户的连接,另一个用于SocketChannel的网络读写。
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024).childHandler(new ChildChannelHandler());
			//绑定端口,同步等待成功
			ChannelFuture f = b.bind(port).sync();
			//等待服务端监听端口关闭;
			f.channel().closeFuture().sync();

		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}

	}

	private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

		@Override
		protected void initChannel(SocketChannel arg0) throws Exception {
			arg0.pipeline().addLast(new TimeServerHandler());
		}

	}

}
    从bind方法看到,这里创建了2个nio的EventLoopGroup实例。EventLoopGroup是一个线程组,它包含了一组NIO线程,专门用于网络事件的处理,实际上他们就是Reactor线程组。这里创建2个的原因是一个用于服务端接受客户端的连接,另一个用于进行SocketChannel的网络读写。ServerBootstrap对象是启动nio服务端的辅助启动类。

    接着设置创建的Channel为NioServerSocketChannel,并设置tcp参数backlog。最后绑定i/o事件的处理类ChildChannleHandler,它的作用类似于Reactor模式中的Handler类,主要用于处理网络i/o事件,例如记录日志、对消息进行编解码等。ght

    finally里面的代码是线程组的关闭,进行优雅的退出。

   TimeServerHandler类继承自ChannelHandlerAdapater,它用于对网络事件进行读写操作。通常我们只需要关注channelRead和exceptionCaught方法。其代码如下:

package zou;

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 {
		//读取客户端发送的字节
				ByteBuf buf = (ByteBuf) msg;
				byte[] req = new byte[buf.readableBytes()];
				buf.readBytes(req);
				String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length());
		
		System.out.println("the time server receive order :" + body + "the counter:" + ++counter);
		String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";
		currentTime += System.getProperty("line.separator");
		ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
		//进行发送消息到客户端
		ctx.writeAndFlush(resp);
	}

	//	@Override
	//	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
	//		//通过调用此方法,将发送的缓冲区的消息全部写到SocketChannel中
	//		ctx.flush();
	//	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}
   在进行读取的时候进行了类型的转换(将字节转换为字符串)。ctx的flush方法是将消息发送队列的消息写入到SocketChannel中发送给对方。从性能的角度思考,为了防止频繁唤醒selector消息进行发送,Netty的write方法并不是直接将消息写入SocketChannel中,而是放入到发送缓冲数组中,再通过调用flush方法将发送缓冲区中的消息全部写到SocketChannel中。另外在捕捉异常的方法中是进行资源的关闭和释放。

    其客户端代码如下:

      

package zou;

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;

public class TimeClient {

	public void connet(int port, String host) throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			//客户端禁用nangle算法
			b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new TimeClientHandle());
				}
			});
			//发起异步连接操作
			ChannelFuture f = b.connect(host, port).sync();
			//等待客户端关闭
			f.channel().closeFuture().sync();

		} finally {
			group.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception {
		int port = 8080;
		if (args != null && args.length > 0) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (Exception e) {

			}
		}
		new TimeClient().connet(port, "127.0.0.1");
	}

}
    这里使用了线程组EventLoopGroup,然后借助辅助类来启动nio,这里设置了SocketChannel,然后设置了tcp参数,禁用了nagle算法,此处通过创建匿名内部类来实现一个ChannelInitalizer类。另外需要实现一个网路处理的handler类。

    handler代码如下:

  

package zou;

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

import java.util.logging.Logger;

public class TimeClientHandle extends ChannelHandlerAdapter {
	private static final Logger logger = Logger.getLogger(TimeClientHandle.class.getName());
	private final ByteBuf firstMessage;
	public TimeClientHandle() {
		byte[] req = "QUERY TIME ORDER".getBytes();
		firstMessage = Unpooled.buffer(req.length);
		firstMessage.writeBytes(req);
	}
	//连接成功后发送指令
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		ctx.writeAndFlush(firstMessage);
	}
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf buf = (ByteBuf) msg;
		byte[] req = new byte[buf.readableBytes()];
		buf.readBytes(req);
		String body = new String(req, "utf-8");
		System.out.println("Now is:" + body);
	}
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		ctx.close();
	}
}

   这里重点关注三个方法:channelActive,channelRead,和exceptionCaught.当客户端和服务端tcp链路建立成功后,Netty的nio线程会调用channelActive方法,发送查询时间指令给服务端,调用writeAndFlush方法将请求消息发送给服务端。当服务端返回应答的时候,channelRead方法被调用。

    需要指出的是这里没有考虑读半包处理问题,对于功能的测试没有问题,但是稍加修改或者压力测试就不能正常工作了。下个例子会进行讲解半包消息的处理。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值