1.netty


是一种网络传输框架,是对NIO的一个封装,一般用于游戏开发,与此相媲美的是MINA。作者都是同一个人。

2.netty的简单原理




java netty客户端服务端 netty做客户端_nio





从上图可以很清晰的看到客户端要与服务通信,必须要一个通道与一个端口才能使其相互通信,boos线程池接收数据分配任务给work线程池进行处理业务逻辑



3.客户端与服务器的通道的粘包与拆包的解决方案,


什么是粘包:


一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU(指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位))的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。处理粘包的唯一方法就是制定应用层的数据通讯协议,通过协议来规范现有接收的数据是否满足消息数据的需要。


解决办法

1、消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。

2、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。

3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段

4、更复杂的自定义应用层协议

下面的则为3的分析



java netty客户端服务端 netty做客户端_nio_02



实例如下:


服务器:




package cn.horace.netty.server;

import io.netty.bootstrap.ServerBootstrap;

/**
 * Netty服务器
 * 
 * @author Administrator
 * 
 */
public class NettyServer {

	// 服务器监听的端口
	private static final int PORT = 1588;

	public static void main(String[] args) {

		// 创建服务器引导对象
		ServerBootstrap bootstrap = new ServerBootstrap();

		// 创建“线程池”
		NioEventLoopGroup bossGroup = new NioEventLoopGroup();
		NioEventLoopGroup workGroup = new NioEventLoopGroup();

		try {
			// 设置“线程池”
			bootstrap.group(bossGroup, workGroup);

			// 告诉Netty它也使用NioServerSocketChannel作为连接通道
			bootstrap.channel(NioServerSocketChannel.class);

			// 当客户端向服务器发送数据的时候都会经过这个方法
			bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel socketChannel) throws Exception {

					/**
					 * 解码器
					 * 
					 * 1、maxFrameLength:最大帧长度值,数据包的最大长度
					 * 2、lengthFieldOffset:长度字段的偏移量
					 * 3、lengthFieldLength:长度子字段的长度值 4、lengthAdjustment:长度调节值
					 * 5、initialBytesToStrip:跳过长度字节值
					 * 6、failFast:当数据包超过maxFrameLength值时
					 * ,是否立即抛出异常,true:立即抛出异常,false:把数据接收完毕后在抛出异常
					 */
					socketChannel.pipeline().addLast(
							new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4, false));

					/**
					 * 编码器
					 * 
					 * 1、lengthFieldLength:长度字段的长度值
					 * 2、lengthIncludesLengthFieldLength:如果为true,那么数据包的长度 =
					 * 长度字段的长度 + 实际数据的长度
					 */
					socketChannel.pipeline().addLast(new LengthFieldPrepender(4, 0, false));

					// 数据包的处理器对象
					socketChannel.pipeline().addLast(new ServerMessageHandler());
				}

			});

			// 同步绑定端口,如果绑定端口失败,则会抛出异常
			ChannelFuture future = bootstrap.bind(PORT).sync();

			System.out.println("Server start at " + PORT + ".");

			// 同步监听服务器端口的关闭
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 优雅的释放线程资源,并关闭服务器
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
	}
}


客户端:



package cn.horace.netty.client;

import io.netty.bootstrap.Bootstrap;

/**
 * Netty客户端
 * 
 * @author Administrator
 * 
 */
public class NettyClient {

	// 服务器监听的端口
	private static final int PORT = 1588;
	private static final String HOST = "127.0.0.1";

	public static void main(String[] args) {

		// 创建客户端引导对象
		Bootstrap bootstrap = new Bootstrap();

		// 创建“线程池”
		NioEventLoopGroup group = new NioEventLoopGroup();

		try {
			// 设置“线程池”
			bootstrap.group(group);

			// 告诉Netty它也使用NioSocketChannel作为连接通道
			bootstrap.channel(NioSocketChannel.class);

			// 当客户端向服务器发送数据的时候都会经过这个方法
			bootstrap.handler(new ChannelInitializer<SocketChannel>() {

				// 这个方法一般是用于做数据包的解码与编码操作
				@Override
				protected void initChannel(SocketChannel socketChannel) throws Exception {

					/**
					 * 解码器
					 * 
					 * 1、maxFrameLength:最大帧长度值,数据包的最大长度
					 * 2、lengthFieldOffset:长度字段的偏移量
					 * 3、lengthFieldLength:长度子字段的长度值 4、lengthAdjustment:长度调节值
					 * 5、initialBytesToStrip:跳过长度字节值
					 * 6、failFast:当数据包超过maxFrameLength值时
					 * ,是否立即抛出异常,true:立即抛出异常,false:把数据接收完毕后在抛出异常
					 */
					socketChannel.pipeline().addLast(
							new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4, false));

					/**
					 * 编码器
					 * 
					 * 1、lengthFieldLength:长度字段的长度值
					 * 2、lengthIncludesLengthFieldLength:如果为true,那么数据包的长度 =
					 * 长度字段的长度 + 实际数据的长度
					 */
					socketChannel.pipeline().addLast(new LengthFieldPrepender(4, 0, false));

					// 数据包的处理器对象
					socketChannel.pipeline().addLast(new ClientMessageHandler());
				}
			});

			// 连接服务器
			ChannelFuture future = bootstrap.connect(new InetSocketAddress(HOST, PORT)).sync();

			System.out.println("Conennect server success.");
			
			// 同步监听服务器端口的关闭
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 优雅的释放线程资源,并关闭客户端
			group.shutdownGracefully();
		}
	}
}


注意:别忘了添加netty-all-4.0.31.Final.jar哦,没有jar可到netty官网http://netty.io/下载,或者前往http://mvnrepository.com/search?q=netty下载