1.什么是粘包和半包
假设我发送一个消息ABC DEF两块消息,此时接收方不一定是通过两个包就接收到两块消息,可能产生的情况有下述几种
例如:
- 一个包内包含ABCDEF的信息,这种叫做粘包
- 也可能分为三个包AB、CD、EF,这种叫做半包
换个角度理解
收发角度来看:一个发送可能会被多次接收,多个发送可能会被一次接收
传输角度来看:一个发送可能占用多个传输包,多个发送可能共用一个传输包
那么会什么会出现粘包和半包现象呢?
2.为什么TCP会出现粘包和半包
总结为一句话:TCP是流式协议,消息无边界
UDP 像邮寄的包裹,虽然一次运输多个,但每个包裹都有“界限”,一个一个签收,所以无粘包、半包问题
粘包的主要原因:
- 发送方每次写入数据 < 套接字缓冲区大小
- 接收方读取套接字缓冲区数据不够及时
半包的主要原因:
- 发送方每次写入数据 > 套接字缓冲区大小
- 发送的数据大于协议的 MTU(Maximum Transmission Unit,最大传输单元),必须拆包

3.解决粘包和半包问题的几种常见方法

4.Netty对三种常用封帧方式的支持

其中固定长度和分割符的方式编码比较简单,因此netty并没有创建具体的类去实现
5.Netty处理粘包、半包的源码

5.1.解码核心工作流程
5.2.解码中两种数据积累器的区别
5.3.三种解码器的常用额外控制参数有哪些
6.二次解码
6.1.什么是二次解码
一次解码负责通过新增长度信息等方式解决粘包、半包的问题,二次解码负责将数据按照一定的编码方式(JSON、XML等)进行序列化及反序列化(例如将报文数据反序列化为Java对象)


6.2.选择编解码方式的要点

6.3.Protobuf 简介与使用
Protobuf 是一个灵活的、高效的用于序列化数据的协议。
相比较 XML 和 JSON 格式,Protobuf 更小、更快、更便捷。
Protobuf 是跨语言的,并且自带了一个编译器(protoc),只需要用它进行编译,可
以自动生成 Java、python、C++ 等代码,不需要再写其他代码。
6.4.netty二次解码的源码
使用示例:io.netty.example.worldclock.WorldClockClientInitializer
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.example.worldclock;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.ssl.SslContext;
public class WorldClockClientInitializer extends ChannelInitializer<SocketChannel> {
private final SslContext sslCtx;
public WorldClockClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc(), WorldClockClient.HOST, WorldClockClient.PORT));
}
p.addLast(new ProtobufVarint32FrameDecoder());
p.addLast(new ProtobufDecoder(WorldClockProtocol.LocalTimes.getDefaultInstance()));
p.addLast(new ProtobufVarint32LengthFieldPrepender());
p.addLast(new ProtobufEncoder());
p.addLast(new WorldClockClientHandler());
}
}