1.netty
从上图可以很清晰的看到客户端要与服务通信,必须要一个通道与一个端口才能使其相互通信,boos线程池接收数据分配任务给work线程池进行处理业务逻辑
3.客户端与服务器的通道的粘包与拆包的解决方案,
什么是粘包:
一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据。TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU(指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位))的往往小于在应用处理的消息数据,所以就会引发一次接收的数据无法满足消息的需要,导致粘包的存在。处理粘包的唯一方法就是制定应用层的数据通讯协议,通过协议来规范现有接收的数据是否满足消息数据的需要。
解决办法
1、消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。
2、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。
3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段
4、更复杂的自定义应用层协议
下面的则为3的分析
实例如下:
服务器:
- 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下载