`
wx1569571209
  • 浏览: 93146 次
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

netty学习之二 分包、组包、粘包处理

 
阅读更多

在数据传输中,我们发送的数据包如下所示

+-----+-----+-----+

| ABC | DEF | GHI |

+-----+-----+-----+

而实际接收的包的格式为:

+----+-------+---+---+ | AB | CDEFG | H | I | +----+-------+---+---+

产生的原因为:数据在传输过程中,产生数据包碎片(TCP/IP数据传输时大数据包无法一次传输,被拆分成小数据包,小数据包即为数据包碎片),这就造成了实际接收的数据包和发送的数据包不一致的情况。

那么一般情况下我们是如何解决这种问题的呢?我所知道的有这几种方案:

  1. 消息定长
  2. 在包尾增加一个标识,通过这个标志符进行分割
  3. 将消息分为两部分,也就是消息头和消息尾,消息头中写入要发送数据的总长度,通常是在消息头的第一个字段使用int值来标识发送数据的长度。
  • 首先看netty中消息定长如何处理的,首先在服务端设定当前服务端接受大小为固定长度,sc.pipeline().addLast(new FixedLengthFrameDecoder(5));本例中指定长度为5个字符串大小,此类是netty框架提供。
    
ServerBootstrap b = new ServerBootstrap();
		b.group(pGroup, cGroup)
		 .channel(NioServerSocketChannel.class)
		 .option(ChannelOption.SO_BACKLOG, 1024)
		 .option(ChannelOption.SO_SNDBUF, 32*1024)
		 .option(ChannelOption.SO_RCVBUF, 32*1024)
		 .childHandler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				//设置定长字符串接收
				sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
				//设置字符串形式的解码
				sc.pipeline().addLast(new StringDecoder());
				sc.pipeline().addLast(new ServerHandler());
			}
		});

客户端代码在创建handler的时候也要指定长度大小,并且与服务器端指定大小一致即可

EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap b = new Bootstrap();
		b.group(group)
		 .channel(NioSocketChannel.class)
		 .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				sc.pipeline().addLast(new FixedLengthFrameDecoder(5));//制定传输数据大小
				sc.pipeline().addLast(new StringDecoder());
				sc.pipeline().addLast(new ClientHandler());
			}
		});
		ChannelFuture cf = b.connect("127.0.0.1", 8765).sync();
		cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaaabbbbb".getBytes()));
		cf.channel().writeAndFlush(Unpooled.copiedBuffer("ccccccccc".getBytes()));

运行结果如下:

输入图片说明

  •   尝试了第一种方案后,下面尝试下第二种方案,这种定长的方式直接限制了传输信息的大小,而且要服务端和客户端同时指定大小感觉并不是太好,下面看下指定分隔符是如何处理的呢,首先服务端指定分隔符ByteBuf buf = Unpooled.copiedBuffer("*_".getBytes());然后创建new DelimiterBasedFrameDecoder(1024, buf)分割符指定Decoder
    
ServerBootstrap b = new ServerBootstrap();
		b.group(pGroup, cGroup)
		 .channel(NioServerSocketChannel.class)
		 .option(ChannelOption.SO_BACKLOG, 1024)
		 .option(ChannelOption.SO_SNDBUF, 32*1024)
		 .option(ChannelOption.SO_RCVBUF, 32*1024)
		 .childHandler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				//设置特殊分隔符
				ByteBuf buf = Unpooled.copiedBuffer("*_".getBytes());
				sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
				//设置字符串形式的解码
				sc.pipeline().addLast(new StringDecoder());
				sc.pipeline().addLast(new ServerHandler());
			}
		});
  • 相同的在客户端需要同样指定客户端分割符decoder,并向服务端发送消息如下
Bootstrap b = new Bootstrap();
		b.group(group)
		 .channel(NioSocketChannel.class)
		 .handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				ByteBuf buf = Unpooled.copiedBuffer("*_".getBytes());
				sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
				sc.pipeline().addLast(new StringDecoder());
				sc.pipeline().addLast(new ClientHandler());
			}
		});
	ChannelFuture cf = b.connect("127.0.0.1", 8765).sync();
		cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bbbasfab*_".getBytes()));
		cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccsdfasfcc*_".getBytes()));
		cf.channel().writeAndFlush(Unpooled.wrappedBuffer("fffff*_".getBytes()));
		//等待客户端端口关闭
		cf.channel().closeFuture().sync();

输入图片说明

下面介绍第三种处理方案,也是很多分布式框架中使用的方式,

Netty4本身自带了ObjectDecoder,ObjectEncoder来实现自定义对象的序列 化, 但是用的是java内置的序列化,由于java序列化的性能并不是很好, 所以很多时候我们需要用其他序列化方式,常见的有 Kryo,Jackson,fastjson,protobuf等。这里要写的其实用什么序列化不是重点,而是我们怎么设计我们的Decoder和 Encoder。

首先我们写一个Encoder,我们继承自MessageToByteEncoder<T> ,把对象转换成byte,继承这个对象,会要求我们实现一个encode方法:

@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
	byte[] body = convertToBytes(msg);  //将对象转换为byte,伪代码,具体用什么进行序列化,你们自行选择。可以使用我上面说的一些
	int dataLength = body.length;  //读取消息的长度
	out.writeInt(dataLength);  //先将消息长度写入,也就是消息头
	out.writeBytes(body);  //消息体中包含我们要发送的数据
}

那么当我们在Decode的时候,该怎么处理发送过来的数据呢?这里我们继承ByteToMessageDecoder方法,继承这个对象,会要求我们实现一个decode方法

public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
	if (in.readableBytes() < HEAD_LENGTH) {  //这个HEAD_LENGTH是我们用于表示头长度的字节数。  由于上面我们传的是一个int类型的值,所以这里HEAD_LENGTH的值为4.
		return;
	}
	in.markReaderIndex();                  //我们标记一下当前的readIndex的位置
	int dataLength = in.readInt();       // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
	if (dataLength < 0) { // 我们读到的消息体长度为0,这是不应该出现的情况,这里出现这情况,关闭连接。
		ctx.close();
	}

	if (in.readableBytes() < dataLength) { //读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
		in.resetReaderIndex();
		return;
	}

	byte[] body = new byte[dataLength];  //  嗯,这时候,我们读到的长度,满足我们的要求了,把传送过来的数据,取出来吧~~
	in.readBytes(body);  //
	Object o = convertToObject(body);  //将byte数据转化为我们需要的对象。伪代码,用什么序列化,自行选择
	out.add(o);  
}

完整代码链接:https://github.com/winstonelei/Smt

转载于:https://my.oschina.net/u/1792341/blog/905363

分享到:
评论

相关推荐

    netty 数据分包、组包、粘包处理机制(部分)1

    Netty 数据分包、组包、粘包处理机制 Netty 中的 LengthFieldBasedFrameDecoder 是一种常用的处理大数据分包传输问题的解决类。该类提供了多种参数来调整帧的解码方式,从而满足不同的应用场景。 1. maxFrame...

    Netty粘包分包服务器端客户端完整例子

    这个项目可能包含了使用`LineBasedFrameDecoder`和`DelimiterBasedFrameDecoder`的服务器端和客户端实现,通过这些文件,开发者可以学习如何在实际项目中应用Netty来处理粘包和分包问题。 总之,理解和正确使用...

    Netty粘包分包现象及解决方案实战,防socket攻击

    Netty粘包分包现象及解决方案实战,防socket攻击水。

    Unity socket 案例包含粘包处理

    "Unity socket案例包含粘包处理"这个标题指向了一个具体的实例,它涉及到在网络通信中如何处理数据包的粘包问题。下面将详细介绍Unity中的Socket通信以及粘包处理的相关知识。 Unity中的Socket通信: Socket是网络...

    Netty粘包拆包问题解决方案

    Netty 粘包拆包问题解决方案 Netty 是一个基于 Java 的网络编程框架,它提供了一个便捷的方式来处理网络数据的读写操作。然而,在使用 Netty 进行网络编程时,经常会遇到粘包和拆包的问题。所谓粘包和拆包,就是指...

    c++服务器 拆包粘包 过程

    在计算机网络编程中,"拆包"和"粘包"是TCP协议中常见的问题,尤其在C++服务器开发中,理解并处理好这两个概念对于构建高效稳定的网络服务至关重要。TCP是一种面向连接的、可靠的传输层协议,它通过流式传输确保数据...

    netty实现微信聊天.zip

    在Netty中,这可以通过使用心跳机制、分包和粘包处理以及状态管理来实现。 1. 心跳机制:为了检测连接是否存活,我们可以定期发送心跳包。如果一段时间内没有收到对方的心跳回应,就可以认为连接已经断开,从而进行...

    netty权威指南中的私有协议栈开发demo

    分包和粘包是网络通信中常见的问题,Netty 提供了解决这些问题的机制。 2. **ByteBuf 使用**:Netty 的 ByteBuf 是一个高效且灵活的字节缓冲区,比 Java 的 ByteBuffer 更适合处理网络数据。理解如何分配、读写、...

    netty学习的几个小例子

    最近学习netty,整理出来的几个小例子: 1、netty_hello,client可在控制台输入信息,server回复; 2、netty_heart,只有server,可用系统自带的telnet连接!10s没有读写会自动踢掉客户端; 3、packagetest,粘包...

    Netty权威指南 PDF电子书下载 带目录书签 完整版.pdf

    ***ty的特点和优势:Netty拥有多种功能和特点,比如高效的I/O线程模型,可配置的线程池,以及对TCP粘包/分包、SSL/TLS协议、编解码器等的内置支持。Netty还提供了一套丰富的协议栈实现,比如HTTP、WebSocket等。它也...

    netty快速入门教程12集 共12集

    Netty 是一个高性能、...通过本套教程的学习,开发者不仅能理解Netty的基本原理和用法,还能了解到如何在实际网络应用中处理粘包、分包问题以及防范Socket攻击,这对于构建高效、安全的网络服务具有重要的实践意义。

    netty通讯实践

    - 可能的实现包括检查数据的完整性,处理分包和粘包问题,以及异常处理。 4. **HostInfo.java 示例** - HostInfo 可能是一个简单的数据类,存储了服务器或客户端的主机信息,如 IP 地址和端口号。在 Netty 中,...

    Java视频教程 Java游戏服务器端开发 Netty NIO AIO Mina视频教程

    11.第十一课粘包分包分析,如何避免socket攻击 12.分析设计一个聊天室的小项目 二、java NIO,AIO编程视频教程 1、java NIO,AIO编程_01.flv 2、java NIO,AIO编程_02.flv 3、java NIO,AIO编程_03.flv 4、java NIO...

    09.读半包处理分析.rar

    通过学习和理解Netty的半包处理以及相关机制,开发者可以构建出更加健壮、高效的网络应用程序。在实际项目中,理解如何处理半包和粘包问题,能够帮助我们更好地设计和实现网络协议,提升系统的稳定性和可靠性。

    基于Java的实例源码-网络应用框架 Netty.zip

    这些编解码器能够自动处理数据的分包和粘包问题,简化了协议处理的复杂性。 4. **EventLoop与EventLoopGroup**:Netty 使用事件驱动模型,EventLoop是单线程的事件处理器,负责处理I/O事件和执行任务。...

    spring-boot 建socket服务器,处理物联网设备的粘包分包

    spring-boot源码

    Netty面试专题1

    分包与重组机制,如 Netty 中的 FrameDecoder。 4. 序列化协议 - **Java自带的序列化**:简单方便,但效率较低,且序列化后的数据不跨平台。 - **protobuf**:Google 开发的高效序列化框架,速度快,数据体积小,...

    netty编码器,解码器实例

    在实际的项目中,可能需要处理更复杂的数据结构和协议,例如,可能需要处理多个不同的消息类型或使用分包和粘包策略。在这种情况下,解码器可能需要实现更复杂的逻辑,如 `FixedLengthFrameDecoder` 或 `...

    Android kotlin使用Netty网络框架实践(客户端、服务端)

    Netty在处理这些情况时,通过内置的编解码器和自动的报文分片聚合机制,有效地解决了这一问题,为开发者提供了一个透明的处理层。 本实践案例为Android开发者提供了一套完整的基于Kotlin语言使用Netty框架进行...

    网络编程中服务端的优化解析

    4. **消息边界协议**:如Netty框架中的LengthFieldBasedFrameDecoder,它允许定义一个长度字段,并自动处理粘包分包问题。服务端可以利用这样的库来简化处理。 优化服务端除了处理粘包分包,还包括其他多个方面: ...

Global site tag (gtag.js) - Google Analytics