Netty 从零到一学习系列之入门篇

5 篇文章 0 订阅
5 篇文章 0 订阅

 Netty开发使用相当的简单,且相当的灵活,开发者不用关心NIO底层的知识,只需要关心业务内容即可。Netty是通过ChannelHandler 进行扩展,开发者只需要实现 ChannelHandler,并把实现类注册到 ChannelPipline中即可。

    ChannelHandler是一个接口,Netty实现了ChanneHandlerAdapter适配器,开发者可以直接继承ChannelHandlerAdapter,只需要重写所关心的方法即可

下面是一个简单但例子,实现客户端请求服务端,获取服务端的当前时间。

1、实现服务端的ChannelHandler。

    服务端创建NettyTimeServerHandler类,此类继承了 ChannelHandlerAdapter,并且重写了channelRead等方法。

服务端具体实现步骤如下:

  1、channelRead 读取客户端发送的信息。

  2、判断读完的客户端信息是否为“QUERY_TIME_ORDER”,如果是则获取服务端当前时间,否则返回 “BAD_ORDER”

  3、调用ChannelHandlerContext 的write 方法发送数据。write只是把数据写入到缓冲区中,并没有真正的输出到网络中。

  4、在channelReadComplete方法中调用ChannelHandlerContext 的flush 方法刷新缓冲区,此方法是把数据真正输出到网络通道中。

package org.java.io.nio.netty;

import java.util.Date;

import org.java.io.utils.Utils;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.CharsetUtil;

public class NettyTimeServerHandler extends ChannelHandlerAdapter {
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("服务端出现异常:" + cause.getMessage());
		ctx.close();
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		System.out.println("我是开始第一个读取的handler---");
		ByteBuf buf = (ByteBuf) msg;
		byte[] bytes = new byte[buf.readableBytes()];
		buf.readBytes(bytes);
		String body = new String(bytes, CharsetUtil.UTF_8);
		System.out.println("接收到的消息:" + body);
		String currentTime = "QUERY_TIME_ORDER".equalsIgnoreCase(body) ? new Date(System.nanoTime()).toString()
					: "BAD_ORDER";
		ByteBuf buffer = Unpooled.copiedBuffer(currentTime.getBytes(CharsetUtil.UTF_8));
		ctx.write(buffer); //把数据发暂时写入到缓冲中
		
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush(); //把缓冲区中的数据刷入到通道中
	}

}

   二、实现客户端ChannelHandler

   客户端创建NettyTimeClientHandler类,此类同样也是继承了 ChannelHandlerAdapter,并且重写了channelRead、channelActive等方法。

客户端端具体实现步骤如下:

      1、当socket连接创建成功后,Netty就会调用channelActive方法,再次方法中调用ChannelHandlerContext的writeAndFlush方法把请求命令发送到网络通道中。

         2、当服务端发送完数据,客户端读取到服务端发送的数据后就会调用channelRead方法,在此方法中获取数据,并且打印到控制台。

package org.java.io.nio.netty;

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

public class NettyTimeClientHandler extends ChannelHandlerAdapter {

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.err.println("客户端出现异常:" + cause.getMessage());
		ctx.close();

	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		byte[] bytes = "QUERY_TIME_ORDER".getBytes(CharsetUtil.UTF_8);
		System.out.println("开始发送信息......");
		ByteBuf buffer = Unpooled.copiedBuffer(bytes);
		ctx.writeAndFlush(buffer); //向缓冲区中写入数据,同时刷新缓冲区数据到I/O通道

	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf buffer = (ByteBuf) msg;
		byte[] bytes = new byte[buffer.readableBytes()];
		buffer.readBytes(bytes);
		System.out.println("返回的信息为:" + new String(bytes, CharsetUtil.UTF_8));
	}

}

  三、实现服务端

   服务端启动类,

  1、此类首先是创建了两个EventLoopGroup类对象,分别为boss 和workers,其中boss是主线程组用来接收客户端的请求,workers是工作线程组用来处理I/O操作。当boss接收到客户端的请求后,即把请求发送给workers线程组。

     2、创建服务端的引导对象ServerBootstrap,此类是辅助启动服务端。

     3、把boss和workers注册到ServerBootstrap中,同时调用ServerBootstrap的其他方法设置参数。

     4、调用ServerBoostrap的bind方法绑定端口,启动服务单。

package org.java.io.nio.netty;


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 NettyServerHandler {

	public void bind(int port) {

		EventLoopGroup boss = new NioEventLoopGroup(); //创建主线程组
		EventLoopGroup workers = new NioEventLoopGroup(); //创建工作线程主

		ServerBootstrap sb = new ServerBootstrap(); //创建服务端引导

		sb.group(boss, workers).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast(new NettyTimeServerHandler()); //把自定义的ChannelHandler注册到ChannelPipeline中
					}
				});
		try {
			ChannelFuture future = sb.bind(port).sync(); //绑定端口
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			boss.shutdownGracefully();
			workers.shutdownGracefully();
		}

	}

	public static void main(String[] args) {
		new NettyServerHandler().bind(8080);
	}

}

  

四、实现客户端

   客户端启动类,说明如下:

    1、此类首先创建了一个EventLoopGroup对象,由于客户端只是连接读写数据,所以值创建了一个EventLoopGroup对象。

    2、创建客户端的引导类Bootstrap对象,同时调用此类的相应方法设置参数。

    3、调用Bootstrap的connect方法连接服务单。

package org.java.io.nio.netty;


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 NettyClientHandler {

	public void connect(String host, int port) {

		EventLoopGroup group = new NioEventLoopGroup();

		Bootstrap bs = new Bootstrap();

		bs.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 NettyTimeClientHandler()); //把自定义的ChannelHandler注册到ChannelPipeline中
					}
				});

		try {
			ChannelFuture future = bs.connect(host, port).sync(); //创建连接
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		new NettyClientHandler().connect("localhost", 8080);
	}

}

 五、运行结果

    1、服务端输出日志:

      

    2、客户端输出日志

        

 

 说明:在学习过程中主要参考了  《Netty实践》、《Netty权威指南第二版》和网络博客。写此博客主要是作为学习笔记,增强记忆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值