netty踩坑--Unpooled.copiedBuffer(ByteBuffer buffer)
netty踩坑Unpooled.copiedBuffer(ByteBuffer buffer)
一、BUG评级
- BUG发现难度:★☆☆☆☆
- BUG无法复现概率:☆☆☆☆☆
- BUG偶然引入概率:★★☆☆☆
- BUG修复,烧脑程度:★★☆☆☆ (随手优化了下代码就不行了T_T)
二、概述
起一个netty服务端,起一个硬件设备,相互通信。
在优化了一堆代码后,发现收不到报文
(这里模拟客户端无法向服务器发送报文)
三、Code review
Channel channel = future.channel();
while (true) {
ByteBuffer finalByteBuffer = ByteBuffer.allocate(2);
finalByteBuffer.put((byte) 66);
finalByteBuffer.put((byte) 88);
// 用下面这个发不出去
ByteBuf byteBuf = Unpooled.copiedBuffer(finalByteBuffer);
// 用下面这2个可以发出去
// ByteBuf byteBuf = Unpooled.copiedBuffer((ByteBuffer) finalByteBuffer.position(0));
// ByteBuf byteBuf = Unpooled.copiedBuffer(finalByteBuffer.array());
channel.writeAndFlush(byteBuf);
TimeUnit.SECONDS.sleep(2);
}
四、BUG分析
来看下这个重载方法的源码
debug发现,代码进入了
if (length == 0) {
return EMPTY_BUFFER;
}
也就是说后续的代码发了个空气~~
回到上面那个方法,
int length = buffer.remaining(); <----看这里
if (length == 0) {
return EMPTY_BUFFER;
}
查看buffer.remaining()
的实现
意思很清楚,返回positon指针到尾部之间的数据,因为上面的put操作移动了position指针,所以返回了
EMPTY_BUFFER
五、完整代码
git链接:https://gitee.com/testnewbie-projects/testnewbie-training-camp/blob/master/code-snippet/src/main/java/cn/testnewbie/training/camp/bug03/CopiedBufferMethod.java
源代码:(需要引入netty包)
package cn.testnewbie.training.camp.bug03;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
public class CopiedBufferMethod {
public static void main(String[] args) throws IOException {
new ServerThread().start();
new ClientThread().start();
System.in.read();
}
static class ServerThread extends Thread {
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup(1);
@Override
public void run() {
ServerBootstrap boot = new ServerBootstrap();
try {
boot.group(boss, worker)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, false)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline()
.addLast(new ByteArrayDecoder())
.addLast(new ByteArrayEncoder())
.addLast(new ServerHandler());
}
})
.bind(9999)
.sync() //阻塞当前线程到服务启动起来
.channel()
.closeFuture()
.sync(); //阻塞当前线程到服务停止
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ClientThread extends Thread {
NioEventLoopGroup group = new NioEventLoopGroup(1);
@Override
public void run() {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
ch.pipeline()
.addLast(new ByteArrayDecoder())
.addLast(new ByteArrayEncoder());
}
});
ChannelFuture f = bootstrap.connect("localhost", 9999);
// 连接状态可以动listener中获取到
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
System.out.println("not connected!");
} else {
Channel channel = future.channel();
while (true) {
ByteBuffer finalByteBuffer = ByteBuffer.allocate(2);
// put的时候,ByteBuffer的position指针会移动
// 导致Unpooled.copiedBuffer(ByteBuffer buffer)返回了EMPTY_BUFFER
finalByteBuffer.put((byte) 66);
finalByteBuffer.put((byte) 88);
// 用下面这个发不出去
ByteBuf byteBuf = Unpooled.copiedBuffer(finalByteBuffer);
// 用下面这2个可以发出去
// ByteBuf byteBuf = Unpooled.copiedBuffer((ByteBuffer) finalByteBuffer.position(0));
// ByteBuf byteBuf = Unpooled.copiedBuffer(finalByteBuffer.array());
channel.writeAndFlush(byteBuf);
TimeUnit.SECONDS.sleep(2);
}
}
}
});
}
}
static class ServerHandler extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(ServerHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
logger.info("channelRead msg = {}", msg);
}
}
}
六、其他博主的详细介绍
https://blog.csdn.net/shuaiAWP/article/details/51915983
作者:放眼江湖
来源链接:https://blog.csdn.net/zlisten1/article/details/115313163
版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。
2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。