Netty5入门学习笔记001

Netty官网:http://netty.io/

本例程使用最新的netty5.x版本编写

服务器端:

TimeServer 时间服务器 服务端接收客户端的连接请求和查询当前时间的指令,判断指令正确后响应返回当前服务器的校准时间。

package c1;

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;

/**
 * server 有粘包问题
 * @author xwalker
 */
public class TimeServer {
	public void bind(int port) throws Exception {
		// 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接
		// 另一个线程组用于处理SocketChannel的网络读写
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			// NIO服务器端的辅助启动类 降低服务器开发难度
			ServerBootstrap serverBootstrap = new ServerBootstrap();
			serverBootstrap.group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)// 类似NIO中serverSocketChannel
					.option(ChannelOption.SO_BACKLOG, 1024)// 配置TCP参数
					.childHandler(new ChildChannelHandler());// 最后绑定I/O事件的处理类
																// 处理网络IO事件

			// 服务器启动后 绑定监听端口 同步等待成功 主要用于异步操作的通知回调 回调处理用的ChildChannelHandler
			ChannelFuture f = serverBootstrap.bind(port).sync();
			System.out.println("timeServer启动");
			// 等待服务端监听端口关闭
			f.channel().closeFuture().sync();

		} finally {
			// 优雅退出 释放线程池资源
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
			System.out.println("服务器优雅的释放了线程资源...");
		}

	}

	/**
	 * 网络事件处理器
	 */
	private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			ch.pipeline().addLast(new TimeServerHandler());
		}

	}

	public static void main(String[] args) throws Exception {
		int port = 8000;
		new TimeServer().bind(port);
	}

}

TimerServer接收到客户端的连接和读写请求后交给处理器handler进行事件的响应处理,服务器定义两组线程组,一组用来处理客户端连接,一组用来处理网络IO事件(SocketChannel)的响应,NioEventLoopGroup是Netty提供的NIO线程组,实际上就是Java NIO中的Reactor线程组。

ServerBootstrap是Netty提供的用于NIO服务端辅助启动类,降低了NIO服务端的开发复杂度。

ServerBootstrap需要绑定服务器网络IO事件的处理类ChildChannelHandler ,用于实际处理具体的IO事件,例如记录日志,对消息编解码等。

TimeServerHandler需要继承Netty提供的适配器ChannelhandlerAdapter重写channelRead等方法完成消息的读写。

package c1;

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

import java.util.Date;
/**
 * server端网络IO事件处理
 * @author xwalker
 *
 */
public class TimeServerHandler extends ChannelHandlerAdapter {
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		System.out.println("服务器读取到客户端请求...");
		ByteBuf buf=(ByteBuf) msg;
		byte[] req=new byte[buf.readableBytes()];
		buf.readBytes(req);
		String body=new String(req,"UTF-8");
		System.out.println("the time server receive order:"+body);
		String curentTime="QUERY TIME ORDER".equalsIgnoreCase(body)?new Date(System.currentTimeMillis()).toString():"BAD ORDER";
		ByteBuf resp=Unpooled.copiedBuffer(curentTime.getBytes());
		ctx.write(resp);
		System.out.println("服务器做出了响应");
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
		System.out.println("服务器readComplete 响应完成");
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		ctx.close();
		System.out.println("服务器异常退出"+cause.getMessage());
	}
}

服务器通过handler接收和处理消息请求,channelRead中的msg就是客户端请求的消息,通过解码获取具体信息后根据消息格式和定义完成后续的响应。

ByteBuf是netty封装和扩展的java NIO中的ByteBuffer类,功能更完善。通过ByteBuf接收和解码msg 转成String类型 然后判断命令是都准确,根据结果做出响应。

客户端:

客户端的处理比较简单,启动客户端,链接服务器成功后发送时间查询的指令,等待服务器响应。

package c1;

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;
/**
 * client 存在TCP粘包问题
 * @author xwlaker
 *
 */
public class TimeClient {
	/**
	 * 连接服务器
	 * @param port
	 * @param host
	 * @throws Exception
	 */
	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<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch)
								throws Exception {
							ch.pipeline().addLast(new TimeClientHandler());
						}
					});
			//异步链接服务器 同步等待链接成功
			ChannelFuture f = b.connect(host, port).sync();
			//等待链接关闭
			f.channel().closeFuture().sync();

		} finally {
			group.shutdownGracefully();
			System.out.println("客户端优雅的释放了线程资源...");
		}

	}

	public static void main(String[] args) throws Exception {
		new TimeClient().connect(8000, "127.0.0.1");
	}

}

客户端定义一组线程组用于处理与服务器的网络IO事件。通过客户端辅助启动类 Bootstrap来配置线程组、TCP参数以及IO事件处理的Handler。

package c1;

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;
/**
 * Client 网络IO事件处理
 * @author xwalker
 *
 */
public class TimeClientHandler extends ChannelHandlerAdapter {
	private static final Logger logger=Logger.getLogger(TimeClientHandler.class.getName());
	private  ByteBuf firstMessage;
	public TimeClientHandler(){
		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);
		System.out.println("客户端active");
	}

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg)
			throws Exception {
		System.out.println("客户端收到服务器响应数据");
		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 channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		ctx.flush();
		System.out.println("客户端收到服务器响应数据处理完成");
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		logger.warning("Unexpected exception from downstream:"+cause.getMessage());
		ctx.close();
		System.out.println("客户端异常退出");
	}
}

TimeClienthandler继承Netty提供的Handler适配器,重写channelActive和channelRead方法 前者通道打开active状态时 发送查询指令,后者接收服务器响应的消息并解码输出。

运行结果:

客户端启动后首先处理器channelActive被调用发送查询指令,服务器端接收到查询指令后返回了当前时间,客户端接收到服务器响应后解码输出当前时间。

时间: 2024-10-09 00:28:52

Netty5入门学习笔记001的相关文章

Netty5入门学习笔记003-TCP粘包/拆包问题的解决之道(下)

TCP网络通信时候会发生粘包/拆包的问题,上节使用定长解码器解码,本次使用Netty提供的特殊分隔符解码器 还是用上节中的代码例子,但是只需要修改一下发送的消息和配置一下解码器就可以了 客户端发送消息中添加分隔符做为指令的结束符,模拟多条指令粘包发出 服务器配置分隔符解码器使用&符号拆包 运行结果: 服务器使用分隔符解码器成功拆包. 当然还有更复杂的自定义协议处理TCP粘包/拆包问题,后续深入学习后在进行讨论. 史上最高性价比PS教程-敬伟Photoshop经典教程

Netty5入门学习笔记004-使用Netty传输POJO对象(上)

使用Netty传输POJO对象,重点在于对象的序列化,序列化后的对象可以通过TCP流进行网络传输,结合Netty提供的对象编解码器,可以做到远程传输对象. 下面我们来看一个例子:模拟订票 首先Java序列化的POJO对象需要实现java.io.Serializable接口. 火车车次和余票量POJO: package bookticket; import java.io.Serializable; /**  * 火车pojo对象  * @author xwalker  */ public cla

Hadoop入门学习笔记---part4

紧接着<Hadoop入门学习笔记---part3>中的继续了解如何用java在程序中操作HDFS. 众所周知,对文件的操作无非是创建,查看,下载,删除.下面我们就开始应用java程序进行操作,前提是按照<Hadoop入门学习笔记---part2>中的已经在虚拟机中搭建好了Hadoop伪分布环境:并且确定现在linux操作系统中hadoop的几个进程已经完全启动了. 好了,废话不多说!实际的例子走起. 在myeclipse中新建一个java工程: 在项目工程中新建一个lib包用于存放

Hadoop入门学习笔记---part1

随着毕业设计的进行,大学四年正式进入尾声.任你玩四年的大学的最后一次作业最后在激烈的选题中尘埃落定.无论选择了怎样的选题,无论最后的结果是怎样的,对于大学里面的这最后一份作业,也希望自己能够尽心尽力,好好做.正是因为选题和hadoop有关,现在正式开始学习hadoop.将笔记整理于此,希望与志同道合的朋友共同交流. 作者:itRed 邮箱:[email protected] 个人博客链接:http://www.cnblogs.com/itred 好了,废话不多说.进入正题!开始hadoop的学习

汇编入门学习笔记 (六)—— si、di,双重循环

疯狂的暑假学习之  汇编入门学习笔记 (六)-- si.di,双重循环 参考: <汇编语言> 王爽 第7章 1. and和or指令,与[bx+idata] and和or,就不多说了. [bx+idata] 这样写是可以的,某些情况下,比较方便. [bx+idata] 也可以写成 idata[bx] 直接见例子: 把'ABcde' 跟 'fGHig' 都改成大写(ASCII中大写字母与小写字母二进制中,只有第五位不同,大写字母是0,小写字母是1) assume cs:code,ds:data d

汇编入门学习笔记 (八)—— 转移指令

疯狂的暑假学习之  汇编入门学习笔记 (八)--  转移指令 參考: <汇编语言> 王爽 第9章 能够改动ip或者同一时候改动cs和ip的指令统称为转移指令. 8086CPU转移行为分为: 段内转移:仅仅改动ip 段间转移:同一时候改动cs和ip 段内转移按ip改动的范围可分为: 短转移:ip改动范围 -128~127 近转移:ip改动范围 -32768~32767 转移指令分为: 无条件转移指令.如 jmp 条件转移指令 循环指令.如 loop 过程. 中断. 1. offset,nop指令

Hadoop入门学习笔记---part3

2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hadoop有了一个基础的了解.但是还是有一些理论性的东西需要重复理解,这样才能彻底的记住它们.个人认为重复是记忆之母.精简一下: NameNode:管理集群,并且记录DataNode文件信息: SecondaryNameNode:可以做冷备份,对一定范围内的数据作快照性备份: DataNode:存储数据:

汇编入门学习笔记 (三) —— 第一个程序

疯狂的暑假学习之  汇编入门学习笔记 (三)-- 第一个程序 参考:<汇编语言> 王爽  第四章 1.一个源程序从写到执行的过程 第一步:编写汇编源程序 第二步:对源程序进行编译连接 第三步:在操作系统中执行 2.源程序 代码: assume cs:first first segment start: mov ax,2 add ax,ax add ax,ax mov ax,4C00H int 21H first ends end start 代码解释: assume .segment.ends

Python 实现 CNKI批量下载 和FireFox Extension 入门学习笔记

?                                 Python 实现 CNKI批量下载 和FireFox Extension 入门学习笔记? 由于需要也是为了督促自己学习新的东西,我原本想要尝试着写一个爬虫程序,能够在cnki上自动得将论文进行批量下载,学习过程中遇到了诸多情况,cnki也真是专业,不得不佩服cnki的强大. 下面进入正题: 学习.实验环境:ubuntu 14.04 工具:Eclipse ,  FireFox, FireBug,HttpFox 编程语言:pyth