在TCP通信底层,当数据进入到缓冲区后,将数据进行组合发送,如果本次发送的数据超过了缓冲区的大小,那么就会将数据拆开,等到下次满足发送条件 再次发送给服务端,所以就会出现这几种情况:
这种属于正常情况,消息1和消息2分别发送
这种就是粘包现象,消息1和消息2在一个数据保中,一起发送到服务端,服务端如果不做特殊处理是区分不出来的
这种就属于拆包现象,消息1被拆成了两半,第一个数据包中只包含消息1的部分信息
代码见视频
服务端和客户端定好每次发送的业务数据包长度,不够长度填充指定的字符,服务端每次按照固定长度进行读取
同样,服务端和客户端定好业务数据分割符,服务端接受数据时,只有当收到分隔符时,才判断分隔符之前的数据是一个完整的业务数据,否则继续等待分隔符
每次发送数据的同时,将数据的长度也带上,但是存放数据长度的位置一定要定好,否则服务端无法识别哪个字节代表的是数据长度 那么 netty 针对这几种方案,自己是如何实现的呢?
注意:所有的编解码器都要放在处理器之前
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.info("客户端channel 初始化");
ch.pipeline().addLast(new FixedLengthFrameDecoder(16));
ch.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture cf = bootstrap.bind(9999).sync();
log.info("服务启动成功....");
cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess()));
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.info("客户端channel 初始化");
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture cf = bootstrap.bind(9999).sync();
log.info("服务启动成功....");
cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess()));
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.info("客户端channel 初始化");
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer("lglbc".getBytes())));
ch.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture cf = bootstrap.bind(9999).sync();
log.info("服务启动成功....");
cf.addListener((ChannelFutureListener) future -> log.info("监听端口:{}",future.isSuccess()));
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
详细见视频
/**
* @Description TODO
* @Author 乐哥聊编程
* @Date 2022/12/10 10:00
*/
@Slf4j
public class NettyMsgDecoder extends ByteToMessageDecoder {
int length=0;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes()>4){
if(length==0){
length=in.readInt();
}
if(in.readableBytes()<length){
log.info("长度不够,继续等待");
return;
}
byte[] content=new byte[length];
in.readBytes(content);
NettyMsg message=new NettyMsg();
message.setLen(length);
message.setContent(content);
// 发送给下一个handler
out.add(message);
length=0;
}
}
}
/**
* @Description TODO
* @Author 乐哥聊编程
* @Date 2022/12/10 10:00
*/
public class NettyMsgEncoder extends MessageToByteEncoder<NettyMsg> {
@Override
protected void encode(ChannelHandlerContext ctx, NettyMsg msg, ByteBuf out) throws Exception {
out.writeInt(msg.getLen());
out.writeBytes(msg.getContent());
}
}