Netty的入门-拆包和粘包的处理

由于是流,拆包和粘包是不可避免的,产生的原因有:1、应用程序write的字节大小大于套接字缓冲区大小;2、进行MMS大小的tcp分段;3、以太网帧的playload大于MTU进行IP分片


解决策略有:1、消息定长;2、包尾添加回车符进行分割;3、将消息分为消息头和消息尾,消息头包含表示消息总长度的字段。


LineBasedFrameDecoder&StringDecoder

// 服务端启动辅助类
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup) // 两个NIO线程组
				.channel(NioServerSocketChannel.class) // 设置要创建的Channel
				.option(ChannelOption.SO_BACKLOG, 1024) // 设置TCP参数
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel socketChannel) throws Exception {
						socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
						socketChannel.pipeline().addLast(new StringDecoder());
						socketChannel.pipeline().addLast(new TimeServerHandler()); // 绑定I/O事件的处理类
					}
				});
			// 绑定端口,同步等待成功
			ChannelFuture f = b.bind(port).sync(); // 只要用于异步操作的通知回调
			// 等待服务端监听端口关闭
			f.channel().closeFuture().sync(); // 等待服务端链路关闭之后退出

Bootstrap b = new Bootstrap();
			b.group(group)
				.channel(NioSocketChannel.class)
				.handler(new ChannelInitializer<SocketChannel>() {

					@Override
					protected void initChannel(SocketChannel ch)
							throws Exception {
						ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
						ch.pipeline().addLast(new StringDecoder());
						ch.pipeline().addLast(new TimeClientHandler());
					}
				});
			// 发起异步连接操作
			ChannelFuture f = b.connect(host, port).sync();
			
			f.channel().closeFuture().sync();

LineBasedFrameDecoder的工作原理是,依次遍历ByteBuf中的可读字节,判断是否有\n 或者 \r\n,如果有就以此为结束为止,从可读索引到结束位置区间的字节组成了一行,它是以换行符为结束标志的解码器,支持携带结束符和不携带结束符两种方式,同时支持配置单行的最大长度,如果连续读取到最大长度后仍然没有发现换行符,则抛出异常,同时忽略之前读到的异常码流。

StringDecoder的工作原理,就是将接收到的对象转换为字符串,然后继续调用后边的handler。


DelimiterBasedFrameDecoder

ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
				.channel(NioServerSocketChannel.class)
				.option(ChannelOption.SO_BACKLOG, 1024)
				.handler(new LoggingHandler())
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch)
							throws Exception {
						ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
						ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
						ch.pipeline().addLast(new StringDecoder());
						ch.pipeline().addLast(new EchoServerHandler());
					}
				});
			ChannelFuture f = b.bind(port).sync();
			f.channel().closeFuture().sync();
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 {
						ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes());
						ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
						ch.pipeline().addLast(new StringDecoder());
						ch.pipeline().addLast(new EchoClientHandler());
					}
				});
			ChannelFuture f = b.connect(host, port).sync();
			f.channel().closeFuture().sync();

DelimiterBasedFrameEncoder有很多构造方法,其中一个参数是单条消息的最大长度,当达到该长度后还没有查找到分隔符就抛出TooLongFrameException,防止由于异常码流导致内存溢出;还有一个参数就是分隔符缓存对象。


FixedLengthFrameDecoder

ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
				.channel(NioServerSocketChannel.class)
				.option(ChannelOption.SO_BACKLOG, 100)
				.handler(new LoggingHandler(LogLevel.INFO))
				.childHandler(new ChannelInitializer<SocketChannel>() {

					@Override
					protected void initChannel(SocketChannel ch)
							throws Exception {
						ch.pipeline().addLast(new FixedLengthFrameDecoder(20));
						ch.pipeline().addLast(new StringDecoder());
						ch.pipeline().addLast(new EchoServerHandler());
					}
				});
			ChannelFuture f = b.bind(port).sync();
			f.channel().closeFuture().sync();
无论一次接收到多少数据,都会按照参数中的固定长度解码,如果是半包消息,则会缓存并且等待下一个包到达。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值