前言
TCP是面向连接的,服务端和客户端通过socket进行数据传输,发送端为了更有效的发送数据,通常会使用Nagle算法把多个数据块合并成一个大的数据块,这样做虽然提高了效率,但是接收端就很难识别完整的数据包了(TCP无消息保护边界),可能会出现粘包拆包的问题。
粘包拆包理解下面我用一个图来带大家理解什么是粘包和拆包 解释一下
- 第一次传输没有问题,数据1和数据2没有粘合,也没有拆分
- 第二次传输,数据1和数据2粘在一起传输了,出现了粘包
- 第三次传输,数据2被分为了2部分,数据2_1 第一份和数据1粘在一起,数据2_2第二份单独传输,这里即出现了拆包也出现了粘包
这里写一个简单案例来演示粘包拆包,客户端发送10个数据包,观察服务端是否做了10次读取,如果不是,就出现粘包或者拆包的情况,这里我们使用byte类型来传输案例如下。
第一步:编写Netty服务端
public static void main(String[] args) {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//添加handler
pipeline.addLast(new ServerHandler());
}
});
try {
ChannelFuture sync = bootstrap.bind(3000).sync();
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
第二步:编写服务端handler
public class ServerHandler extends SimpleChannelInboundHandler {
//服务端接收次数
private int num = 0;
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("接收消息,次数 = "+ num++);
//接收数据
byte[] bytes = new byte[msg.readableBytes()];
//把数据读到bytes中
msg.readBytes(bytes);
System.out.println(new String(bytes, CharsetUtil.UTF_8));
}
}
这里定义了一个num来记录服务端数据读取次数。
第三步:定义Netty客户端
public static void main(String[] args) {
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new ClientHandler());
}
});
ChannelFuture sync = null;
try {
sync = bootstrap.connect("127.0.0.1", 3000).sync();
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
第四步:定义客户端的Handler
public class ClientHandler extends SimpleChannelInboundHandler {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//发送10个数据块
for (int i = 0; i
关注
打赏
热门博文
- 十九.SpringCloud极简入门-基于Docker整合ELK分布式日志解决方案
- 八.全文检索ElasticSearch经典入门-深入理解ElasticSearch核心原理
- 七.全文检索ElasticSearch经典入门-聚合查询
- 六.全文检索ElasticSearch经典入门-高亮
- 招人啦招人啦快来成为我的同事
- 四.全文检索ElasticSearch经典入门-字符串查询&批量查询&DSL查询过滤&乐观锁
- 三.全文检索ElasticSearch经典入门-索引CRUD&分词器&文档映射&文档CRUD
- 二.全文检索ElasticSearch经典入门-倒排索引&核心概念&集群概念&分片机制
- 使用canal解决Mysql和ElasticSearch数据同步问题
- 五.MongoDB入门-SpringData操作MongoDB