目录
粘包、半包
相关概念
网络数据发送和接收过程
Netty半包粘包解决方案
ByteBuf获取和默认大小
短链接
固定长度
固定分隔符
预设长度
常见协议代码举例
redis协议
http协议
参考链接
粘包、半包
相关概念
程序处理过程中我们会通过缓冲区接收数据,接收的过程中可能会出现所谓的粘包和半包的问题。
当发送方发送数据大小小于接收方缓冲区大小且接收方来不及处理缓冲区中的内容,此时可能发生发送方发送多个完整的消息到接收方缓冲区中,接收方误将其作为一次发送的消息处理,此时产生粘包现象。
当发送方数据大小大于接收方缓冲区大小时,发送方数据并不能一次全部发送到接收方缓冲区中,此时发送方数据就会被拆分为不同的数据大小进行发送,此时产生半包现象。
网络数据发送和接收过程
通信过程有一个客户端和一个接收端,站在客户端角度来讲,我们可以分为这样的几个层次,用户区或者叫用户态,系统区或者内核区或者叫内核态,再往下是硬件或则叫网卡。
Java程序是在用户区,socketChnnel.write(byte)这个时候就是把数据复制到我们的内核态当中,然后操作系统将数据再次拷贝给网卡,网卡操作数据可以传递出去。真正发送的时候会以数据包的形式进行发送,当然数据包的这个概念是不准确的,应该叫做数据帧。
接收方数据拿到之后,由网卡拿到数据之后将数据拷贝到操作系统的数据的缓冲区,也就是socket的缓冲区,当我们调用一些读的方法的时候,Socket缓冲区的数据会拷贝到我们的用户区里边。
用户内存和我们的Socket的缓冲区这两个都有可能产生一个半包粘包的问题(数据要么存少了,要么存多了)
DMA(Direct Memory Access)直接内存访问,是一种计算机硬件特性,允许某些硬件设备(磁盘驱动器、网络接口卡等)直接向内存传输数据,而不需要中央处理器CPU的介入。能够显著提高数据传输的效率,能够减少CPU负载,允许CPU在数据传输期间执行其他任务。
Netty半包粘包解决方案
粘包:是由于数据发送太快,接收方byteBuf太大。
半包:MSS、接收方byteBuf太小。
ByteBuf获取和默认大小
ByteBuf是Netty通过Handler帮我们创建的(第一个非head的InboundHandler获取的参数就是Netty获取的数据),Netty接收数据帮我们创建的ByteBuf的大小默认是1024B,可以指定最小字节16,同时也可以指定(最小值、初始值、最大值)。
我们可以人为地设置Socket缓冲区和ByteBuf缓冲区,从而更好地验证Netty在网络传输中出现的半包、粘包现象以及其解决方案。
Soket缓冲区的改变是一个全局的概念。ChannelOption.SO_REVBUF代表的是接收缓冲区的大小。channelOption.SO_SNDBUF代表的是发送缓冲区的大小。SO代表的是Socket,通过这种方式所做的修改是在应用程序层面做出的修改,不会上升到全局的角度,这样不会影响到操作系统下其他的TCP协议通信的程序。
短链接
短链接:发一个包建立一个连接,这样连接建立到断开之间就是消息边界。人为的让连接建立和断开,连接断开后服务端读取到的消息长度为-1,此时服务器端就知道客户端断开,读取的消息为一次完整的消息。
局限性:发一次包建立一次连接,效率太低;短连接能够解决粘包问题,当ByteBuf太小时,对于半包的处理无能为力。
服务器端代码:
@Slf4j
public class HelloServer {
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.group(boss, worker);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("Server error ", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
客户端代码:
public class HelloClient {
static final Logger log = LoggerFactory.getLogger(HelloClient.class);
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
send();
}
}
private static void send() {
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
// channelActive会在连接channel建立成功后,会触发 active事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buffer = ctx.alloc().buffer(10);
buffer.writeBytes(new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15});
ctx.writeAndFlush(buffer);
ctx.channel().close();
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 8080));
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.info("clinet error ", e);
} finally {
worker.shutdownGracefully();
}
}
}
结果:
00:01:43.237 [nioEventLoopGroup-3-8] INFO io.netty.handler.logging.LoggingHandler - [id: 0x700958ce, L:/127.0.0.1:8080 - R:/127.0.0.1:62462] READ COMPLETE
00:01:43.237 [nioEventLoopGroup-3-8] INFO io.netty.handler.logging.LoggingHandler - [id: 0x700958ce, L:/127.0.0.1:8080 - R:/127.0.0.1:62462] READ COMPLETE
00:01:43.237 [nioEventLoopGroup-3-8] INFO io.netty.handler.logging.LoggingHandler - [id: 0x700958ce, L:/127.0.0.1:8080 ! R:/127.0.0.1:62462] INACTIVE
00:01:43.238 [nioEventLoopGroup-3-8] INFO io.netty.handler.logging.LoggingHandler - [id: 0x700958ce, L:/127.0.0.1:8080 ! R:/127.0.0.1:62462] UNREGISTERED
00:01:43.255 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591] REGISTERED
00:01:43.256 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591] ACTIVE
00:01:43.268 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591] READ: 16B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
+--------+-------------------------------------------------+----------------+
00:01:43.268 [nioEventLoopGroup-3-9] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message SimpleLeakAwareByteBuf(PooledUnsafeDirectByteBuf(ridx: 0, widx: 16, cap: 1024)) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:01:43.268 [nioEventLoopGroup-3-9] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591].
00:01:43.268 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591] READ COMPLETE
00:01:43.268 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 - R:/127.0.0.1:62591] READ COMPLETE
00:01:43.268 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 ! R:/127.0.0.1:62591] INACTIVE
00:01:43.268 [nioEventLoopGroup-3-9] INFO io.netty.handler.logging.LoggingHandler - [id: 0xc73537e9, L:/127.0.0.1:8080 ! R:/127.0.0.1:62591] UNREGISTERED
00:01:43.285 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720] REGISTERED
00:01:43.285 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720] ACTIVE
00:01:43.298 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720] READ: 16B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
+--------+-------------------------------------------------+----------------+
00:01:43.298 [nioEventLoopGroup-3-10] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledUnsafeDirectByteBuf(ridx: 0, widx: 16, cap: 1024) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:01:43.298 [nioEventLoopGroup-3-10] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720].
00:01:43.298 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720] READ COMPLETE
00:01:43.298 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 - R:/127.0.0.1:62720] READ COMPLETE
00:01:43.299 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 ! R:/127.0.0.1:62720] INACTIVE
00:01:43.299 [nioEventLoopGroup-3-10] INFO io.netty.handler.logging.LoggingHandler - [id: 0xcd634dbe, L:/127.0.0.1:8080 ! R:/127.0.0.1:62720] UNREGISTERED
固定长度
固定长度首先约定发送方发送的所有数据包长度固定,均为一个常量,随后接收方借助定长解码器【FixedLengthFrameDecoder】解析获取到的定长消息。
FixedLengthFrameDecoder:接收方收到数据后首先将其缓存起来,随后提取固定长度后解码处理。
可以通过如下命令设置接收方的定长解码器:
socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(10));
服务端代码:
@Slf4j
public class HelloServer {
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.group(boss, worker);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(10));
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("Server error ", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
客户端代码:
public class HelloClient {
static final Logger log = LoggerFactory.getLogger(HelloClient.class);
public static void main(String[] args) {
send();
}
private static byte[] fillChar(char ch, int lenght){
byte[] ans = new byte[10];
Arrays.fill(ans, (byte) '_');
for(int i = 0; i < lenght; i++){
ans[i] = ((byte) ch);
}
for (int i = 0; i < ans.length; i++) {
System.out.print((char) ans[i]);
}
System.out.println();
return ans;
}
private static void send() {
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
// channelActive会在连接channel建立成功后,会触发 active事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buffer = ctx.alloc().buffer(10);
for (int i = 0; i < 10; i++) {
char ch = (char) ('a' + i);
buffer.writeBytes(fillChar(ch, i + 1));
// ctx.channel().close();
}
ctx.writeAndFlush(buffer);
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 8080));
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.info("clinet error ", e);
} finally {
worker.shutdownGracefully();
}
}
}
结果:
00:10:24.650 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledSlicedByteBuf(ridx: 0, widx: 10, cap: 10/10, unwrapped: PooledUnsafeDirectByteBuf(ridx: 40, widx: 100, cap: 1024)) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:10:24.650 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [FixedLengthFrameDecoder#0, LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0x4824e86f, L:/127.0.0.1:8080 - R:/127.0.0.1:64332].
00:10:24.650 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4824e86f, L:/127.0.0.1:8080 - R:/127.0.0.1:64332] READ: 10B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 65 65 65 65 65 5f 5f 5f 5f 5f |eeeee_____ |
+--------+-------------------------------------------------+----------------+
00:10:24.650 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledSlicedByteBuf(ridx: 0, widx: 10, cap: 10/10, unwrapped: PooledUnsafeDirectByteBuf(ridx: 50, widx: 100, cap: 1024)) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:10:24.650 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [FixedLengthFrameDecoder#0, LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0x4824e86f, L:/127.0.0.1:8080 - R:/127.0.0.1:64332].
00:10:24.650 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x4824e86f, L:/127.0.0.1:8080 - R:/127.0.0.1:64332] READ: 10B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 66 66 66 66 66 66 5f 5f 5f 5f |ffffff____ |
+--------+-------------------------------------------------+----------------+
固定分隔符
发送方发送数据后在数据末尾添加固定标识,用以向接收方严明此位置为数据的结束位置,随后接收方根据特定的分隔符分割并接收数据。
固定分隔符主要有以下两个类别:
-
LineBasedFrameDecoder:默认以'\n'或者'\r\n'作为分隔符。 DelimiterBasedFrameDecoder :支持自定义分隔符。
上述两种分隔符使用的时候均需要设置最大长度,达到最大长度后仍然没有出现分隔符,则抛出异常。
服务端代码:
@Slf4j
public class HelloServer {
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.group(boss, worker);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("Server error ", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
客户端代码:
public class HelloClient {
static final Logger log = LoggerFactory.getLogger(HelloClient.class);
public static void main(String[] args) {
send();
}
private static void send() {
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
// channelActive会在连接channel建立成功后,会触发 active事件
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buffer = ctx.alloc().buffer();
Random random = new Random();
char ch = 'a';
for (int i = 0; i < 10; i++) {
for(int j = 0; j < random.nextInt(16) + 1; j++){
buffer.writeByte(ch);
}
buffer.writeBytes("\n".toString().getBytes());
ch++;
}
ctx.writeAndFlush(buffer);
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress("localhost", 8080));
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
log.info("clinet error ", e);
} finally {
worker.shutdownGracefully();
}
}
}
结果:
#客户端结果
00:18:56.518 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x13d89048, L:/127.0.0.1:61146 - R:localhost/127.0.0.1:8080] WRITE: 71B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 61 61 61 61 61 61 61 0a 62 62 62 62 62 62 62 |aaaaaaaa.bbbbbbb|
|00000010| 0a 63 63 63 63 63 0a 64 64 64 64 64 64 64 64 64 |.ccccc.ddddddddd|
|00000020| 64 0a 65 65 65 65 65 0a 66 66 0a 67 67 67 67 67 |d.eeeee.ff.ggggg|
|00000030| 0a 68 68 68 68 68 68 0a 69 69 69 69 69 69 69 0a |.hhhhhh.iiiiiii.|
|00000040| 6a 6a 6a 6a 6a 6a 0a |jjjjjj. |
+--------+-------------------------------------------------+----------------+
00:18:56.518 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x13d89048, L:/127.0.0.1:61146 - R:localhost/127.0.0.1:8080] FLUSH
#服务端结果
00:18:56.534 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0xf0a94455, L:/127.0.0.1:8080 - R:/127.0.0.1:61146] READ: 8B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 61 61 61 61 61 61 61 |aaaaaaaa |
+--------+-------------------------------------------------+----------------+
00:18:56.534 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledSlicedByteBuf(ridx: 0, widx: 8, cap: 8/8, unwrapped: PooledUnsafeDirectByteBuf(ridx: 9, widx: 71, cap: 1024)) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:18:56.534 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LineBasedFrameDecoder#0, LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xf0a94455, L:/127.0.0.1:8080 - R:/127.0.0.1:61146].
00:18:56.534 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0xf0a94455, L:/127.0.0.1:8080 - R:/127.0.0.1:61146] READ: 7B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 62 62 62 62 62 62 62 |bbbbbbb |
+--------+-------------------------------------------------+----------------+
00:18:56.534 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledSlicedByteBuf(ridx: 0, widx: 7, cap: 7/7, unwrapped: PooledUnsafeDirectByteBuf(ridx: 17, widx: 71, cap: 1024)) that reached at the tail of the pipeline. Please check your pipeline configuration.
00:18:56.534 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LineBasedFrameDecoder#0, LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xf0a94455, L:/127.0.0.1:8080 - R:/127.0.0.1:61146].
00:18:56.534 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0xf0a94455, L:/127.0.0.1:8080 - R:/127.0.0.1:61146] READ: 5B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 63 63 63 63 63 |ccccc |
+--------+-------------------------------------------------+----------------+
预设长度
基于长度的帧解码器:
使用时需要指定四个长度:
- lengthFieldOffset:= 1 :偏移量(长度内容所在位置的距离开始处的偏移量的大小)
- lengthFieldLength:= 2:代表的是标识长度的这个数据所占用的数据长度。
- lengthAdjustment: = 1:长度内容到具体消息内容的偏移量。
- initialBytesToStrip:= 3:需要剥离头信息的长度。(是不是需要剥离头,自定)一般我们是要头的。
使用EmbededChannel编写演示代码
/**
* 使用EmbededChannel测试LengthFieldDecoder
*/
public class TestLengthFieldDecoder {
public static void main(String[] args) {
EmbeddedChannel channel = new EmbeddedChannel(
new LengthFieldBasedFrameDecoder(
1024, 0, 4, 0, 4),
new LoggingHandler(LogLevel.INFO)
);
ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
buildBuffer(buffer, "Hello, world");
buildBuffer(buffer, "Hi, Hi");
channel.writeInbound(buffer);
}
private static void buildBuffer(ByteBuf buffer, String str) {
byte[] bytes = str.toString().getBytes();
int length = bytes.length;
buffer.writeInt(length);
buffer.writeBytes(bytes);
}
}
结果
00:25:25.154 [main] INFO io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] READ: 12B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 |Hello, world |
+--------+-------------------------------------------------+----------------+
00:25:25.154 [main] INFO io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] READ: 6B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 69 2c 20 48 69 |Hi, Hi |
+--------+-------------------------------------------------+----------------+
00:25:25.154 [main] INFO io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] READ COMPLETE
常见协议代码举例
redis协议
代码:
/**
* set name zhangsan
* *3
* $3
* set
* $4
* name
* $8
* zhangsan
*/
public class TestRedis {
final static byte[] LINE = {13, 10};
public static void main(String[] args) {
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ByteBuf buf = ctx.alloc().buffer();
buf.writeBytes("auth 123456".getBytes()).writeBytes(LINE);
ctx.writeAndFlush(buf);
ByteBuf buffer = ctx.alloc().buffer();
// super.channelInactive(ctx);
buffer.writeBytes("*3".getBytes()).writeBytes(LINE);
buffer.writeBytes("$3".getBytes()).writeBytes(LINE);
buffer.writeBytes("set".getBytes()).writeBytes(LINE);
buffer.writeBytes("$4".getBytes()).writeBytes(LINE);
buffer.writeBytes("name".getBytes()).writeBytes(LINE);
buffer.writeBytes("$8".getBytes()).writeBytes(LINE);
buffer.writeBytes("zhangsan".getBytes()).writeBytes(LINE);
ctx.writeAndFlush(buffer);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println(buf.toString(Charset.defaultCharset()));
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("localhost", 6379).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
worker.shutdownGracefully();
}
}
}
结果:
00:27:04.468 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] WRITE: 13B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 61 75 74 68 20 31 32 33 34 35 36 0d 0a |auth 123456.. |
+--------+-------------------------------------------------+----------------+
00:27:04.469 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] FLUSH
00:27:04.469 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] WRITE: 37B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 2a 33 0d 0a 24 33 0d 0a 73 65 74 0d 0a 24 34 0d |*3..$3..set..$4.|
|00000010| 0a 6e 61 6d 65 0d 0a 24 38 0d 0a 7a 68 61 6e 67 |.name..$8..zhang|
|00000020| 73 61 6e 0d 0a |san.. |
+--------+-------------------------------------------------+----------------+
00:27:04.469 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] FLUSH
00:27:04.470 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] READ: 10B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 2b 4f 4b 0d 0a 2b 4f 4b 0d 0a |+OK..+OK.. |
+--------+-------------------------------------------------+----------------+
+OK
+OK
00:27:04.470 [nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x92af23ed, L:/127.0.0.1:62317 - R:localhost/127.0.0.1:6379] READ COMPLETE
http协议
代码:
@Slf4j
public class TestHttp {
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.group(boss, worker);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
// Http编解码器
ch.pipeline().addLast(new HttpServerCodec());
// 入站处理器,可以只关心某一种类型的消息,根据消息的类型进行选择处理
ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
// 获取请求
log.info(msg.uri());
// 构建响应
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(msg.protocolVersion(), HttpResponseStatus.OK);
byte[] bytes = "<h1>Hello, world</h1>".getBytes();
httpResponse.headers().setInt(CONTENT_LENGTH, bytes.length);
httpResponse.content().writeBytes(bytes);
// 写回响应
ctx.writeAndFlush(httpResponse);
}
});
/* ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("{}", msg.getClass());
}
});*/
}
});
ChannelFuture channelFuture = bootstrap.bind(8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
// e.printStackTrace();
log.error("server error : ", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
结果:
00:28:10.860 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] READ: 1002B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 68 65 6c 6c 6f 20 48 54 54 50 2f |GET /hello HTTP/|
|00000010| 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c |1.1..Host: local|
|00000020| 68 6f 73 74 3a 38 30 38 30 0d 0a 43 6f 6e 6e 65 |host:8080..Conne|
|00000030| 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69 76 |ction: keep-aliv|
|00000040| 65 0d 0a 73 65 63 2d 63 68 2d 75 61 3a 20 22 4d |e..sec-ch-ua: "M|
|00000050| 69 63 72 6f 73 6f 66 74 20 45 64 67 65 22 3b 76 |icrosoft Edge";v|
|00000060| 3d 22 31 32 35 22 2c 20 22 43 68 72 6f 6d 69 75 |="125", "Chromiu|
|00000070| 6d 22 3b 76 3d 22 31 32 35 22 2c 20 22 4e 6f 74 |m";v="125", "Not|
|00000080| 2e 41 2f 42 72 61 6e 64 22 3b 76 3d 22 32 34 22 |.A/Brand";v="24"|
|00000090| 0d 0a 73 65 63 2d 63 68 2d 75 61 2d 6d 6f 62 69 |..sec-ch-ua-mobi|
|000000a0| 6c 65 3a 20 3f 30 0d 0a 73 65 63 2d 63 68 2d 75 |le: ?0..sec-ch-u|
|000000b0| 61 2d 70 6c 61 74 66 6f 72 6d 3a 20 22 57 69 6e |a-platform: "Win|
|000000c0| 64 6f 77 73 22 0d 0a 55 70 67 72 61 64 65 2d 49 |dows"..Upgrade-I|
|000000d0| 6e 73 65 63 75 72 65 2d 52 65 71 75 65 73 74 73 |nsecure-Requests|
|000000e0| 3a 20 31 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a |: 1..User-Agent:|
|000000f0| 20 4d 6f 7a 69 6c 6c 61 2f 35 2e 30 20 28 57 69 | Mozilla/5.0 (Wi|
|00000100| 6e 64 6f 77 73 20 4e 54 20 31 30 2e 30 3b 20 57 |ndows NT 10.0; W|
|00000110| 69 6e 36 34 3b 20 78 36 34 29 20 41 70 70 6c 65 |in64; x64) Apple|
|00000120| 57 65 62 4b 69 74 2f 35 33 37 2e 33 36 20 28 4b |WebKit/537.36 (K|
|00000130| 48 54 4d 4c 2c 20 6c 69 6b 65 20 47 65 63 6b 6f |HTML, like Gecko|
|00000140| 29 20 43 68 72 6f 6d 65 2f 31 32 35 2e 30 2e 30 |) Chrome/125.0.0|
|00000150| 2e 30 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 |.0 Safari/537.36|
|00000160| 20 45 64 67 2f 31 32 35 2e 30 2e 30 2e 30 0d 0a | Edg/125.0.0.0..|
|00000170| 41 63 63 65 70 74 3a 20 74 65 78 74 2f 68 74 6d |Accept: text/htm|
|00000180| 6c 2c 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 68 |l,application/xh|
|00000190| 74 6d 6c 2b 78 6d 6c 2c 61 70 70 6c 69 63 61 74 |tml+xml,applicat|
|000001a0| 69 6f 6e 2f 78 6d 6c 3b 71 3d 30 2e 39 2c 69 6d |ion/xml;q=0.9,im|
|000001b0| 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 2f 77 |age/avif,image/w|
|000001c0| 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 2c 2a |ebp,image/apng,*|
|000001d0| 2f 2a 3b 71 3d 30 2e 38 2c 61 70 70 6c 69 63 61 |/*;q=0.8,applica|
|000001e0| 74 69 6f 6e 2f 73 69 67 6e 65 64 2d 65 78 63 68 |tion/signed-exch|
|000001f0| 61 6e 67 65 3b 76 3d 62 33 3b 71 3d 30 2e 37 0d |ange;v=b3;q=0.7.|
|00000200| 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 65 3a |.Sec-Fetch-Site:|
|00000210| 20 6e 6f 6e 65 0d 0a 53 65 63 2d 46 65 74 63 68 | none..Sec-Fetch|
|00000220| 2d 4d 6f 64 65 3a 20 6e 61 76 69 67 61 74 65 0d |-Mode: navigate.|
|00000230| 0a 53 65 63 2d 46 65 74 63 68 2d 55 73 65 72 3a |.Sec-Fetch-User:|
|00000240| 20 3f 31 0d 0a 53 65 63 2d 46 65 74 63 68 2d 44 | ?1..Sec-Fetch-D|
|00000250| 65 73 74 3a 20 64 6f 63 75 6d 65 6e 74 0d 0a 41 |est: document..A|
|00000260| 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e 67 3a 20 |ccept-Encoding: |
|00000270| 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 2c 20 62 |gzip, deflate, b|
|00000280| 72 2c 20 7a 73 74 64 0d 0a 41 63 63 65 70 74 2d |r, zstd..Accept-|
|00000290| 4c 61 6e 67 75 61 67 65 3a 20 7a 68 2d 43 4e 2c |Language: zh-CN,|
|000002a0| 7a 68 3b 71 3d 30 2e 39 2c 65 6e 3b 71 3d 30 2e |zh;q=0.9,en;q=0.|
|000002b0| 38 2c 65 6e 2d 47 42 3b 71 3d 30 2e 37 2c 65 6e |8,en-GB;q=0.7,en|
|000002c0| 2d 55 53 3b 71 3d 30 2e 36 0d 0a 43 6f 6f 6b 69 |-US;q=0.6..Cooki|
|000002d0| 65 3a 20 49 64 65 61 2d 39 66 61 62 39 61 62 30 |e: Idea-9fab9ab0|
|000002e0| 3d 35 32 31 66 38 65 33 38 2d 62 34 32 35 2d 34 |=521f8e38-b425-4|
|000002f0| 63 62 36 2d 39 63 39 63 2d 32 61 65 64 31 66 38 |cb6-9c9c-2aed1f8|
|00000300| 64 32 66 38 30 3b 20 58 58 4c 5f 4a 4f 42 5f 4c |d2f80; XXL_JOB_L|
|00000310| 4f 47 49 4e 5f 49 44 45 4e 54 49 54 59 3d 37 62 |OGIN_IDENTITY=7b|
|00000320| 32 32 36 39 36 34 32 32 33 61 33 31 32 63 32 32 |226964223a312c22|
|00000330| 37 35 37 33 36 35 37 32 36 65 36 31 36 64 36 35 |757365726e616d65|
|00000340| 32 32 33 61 32 32 36 31 36 34 36 64 36 39 36 65 |223a2261646d696e|
|00000350| 32 32 32 63 32 32 37 30 36 31 37 33 37 33 37 37 |222c227061737377|
|00000360| 36 66 37 32 36 34 32 32 33 61 32 32 36 35 33 31 |6f7264223a226531|
|00000370| 33 30 36 31 36 34 36 33 33 33 33 39 33 34 33 39 |3061646333393439|
|00000380| 36 32 36 31 33 35 33 39 36 31 36 32 36 32 36 35 |6261353961626265|
|00000390| 33 35 33 36 36 35 33 30 33 35 33 37 36 36 33 32 |3536653035376632|
|000003a0| 33 30 36 36 33 38 33 38 33 33 36 35 32 32 32 63 |306638383365222c|
|000003b0| 32 32 37 32 36 66 36 63 36 35 32 32 33 61 33 31 |22726f6c65223a31|
|000003c0| 32 63 32 32 37 30 36 35 37 32 36 64 36 39 37 33 |2c227065726d6973|
|000003d0| 37 33 36 39 36 66 36 65 32 32 33 61 36 65 37 35 |73696f6e223a6e75|
|000003e0| 36 63 36 63 37 64 0d 0a 0d 0a |6c6c7d.... |
+--------+-------------------------------------------------+----------------+
00:28:10.871 [nioEventLoopGroup-3-1] INFO com.example.code.updatenetty.c6http.TestHttp - /hello
00:28:10.874 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] WRITE: 60B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 63 6f 6e 74 65 6e 74 2d 6c 65 6e 67 74 68 3a |.content-length:|
|00000020| 20 32 31 0d 0a 0d 0a 3c 68 31 3e 48 65 6c 6c 6f | 21....<h1>Hello|
|00000030| 2c 20 77 6f 72 6c 64 3c 2f 68 31 3e |, world</h1> |
+--------+-------------------------------------------------+----------------+
00:28:10.875 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] FLUSH
00:28:10.875 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message EmptyLastHttpContent that reached at the tail of the pipeline. Please check your pipeline configuration.
00:28:10.875 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LoggingHandler#0, HttpServerCodec#0, TestHttp$1$1#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348].
00:28:10.875 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] READ COMPLETE
00:28:10.931 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] READ: 928B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 47 45 54 20 2f 66 61 76 69 63 6f 6e 2e 69 63 6f |GET /favicon.ico|
|00000010| 20 48 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a | HTTP/1.1..Host:|
|00000020| 20 6c 6f 63 61 6c 68 6f 73 74 3a 38 30 38 30 0d | localhost:8080.|
|00000030| 0a 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 |.Connection: kee|
|00000040| 70 2d 61 6c 69 76 65 0d 0a 73 65 63 2d 63 68 2d |p-alive..sec-ch-|
|00000050| 75 61 3a 20 22 4d 69 63 72 6f 73 6f 66 74 20 45 |ua: "Microsoft E|
|00000060| 64 67 65 22 3b 76 3d 22 31 32 35 22 2c 20 22 43 |dge";v="125", "C|
|00000070| 68 72 6f 6d 69 75 6d 22 3b 76 3d 22 31 32 35 22 |hromium";v="125"|
|00000080| 2c 20 22 4e 6f 74 2e 41 2f 42 72 61 6e 64 22 3b |, "Not.A/Brand";|
|00000090| 76 3d 22 32 34 22 0d 0a 73 65 63 2d 63 68 2d 75 |v="24"..sec-ch-u|
|000000a0| 61 2d 6d 6f 62 69 6c 65 3a 20 3f 30 0d 0a 55 73 |a-mobile: ?0..Us|
|000000b0| 65 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c 6c |er-Agent: Mozill|
|000000c0| 61 2f 35 2e 30 20 28 57 69 6e 64 6f 77 73 20 4e |a/5.0 (Windows N|
|000000d0| 54 20 31 30 2e 30 3b 20 57 69 6e 36 34 3b 20 78 |T 10.0; Win64; x|
|000000e0| 36 34 29 20 41 70 70 6c 65 57 65 62 4b 69 74 2f |64) AppleWebKit/|
|000000f0| 35 33 37 2e 33 36 20 28 4b 48 54 4d 4c 2c 20 6c |537.36 (KHTML, l|
|00000100| 69 6b 65 20 47 65 63 6b 6f 29 20 43 68 72 6f 6d |ike Gecko) Chrom|
|00000110| 65 2f 31 32 35 2e 30 2e 30 2e 30 20 53 61 66 61 |e/125.0.0.0 Safa|
|00000120| 72 69 2f 35 33 37 2e 33 36 20 45 64 67 2f 31 32 |ri/537.36 Edg/12|
|00000130| 35 2e 30 2e 30 2e 30 0d 0a 73 65 63 2d 63 68 2d |5.0.0.0..sec-ch-|
|00000140| 75 61 2d 70 6c 61 74 66 6f 72 6d 3a 20 22 57 69 |ua-platform: "Wi|
|00000150| 6e 64 6f 77 73 22 0d 0a 41 63 63 65 70 74 3a 20 |ndows"..Accept: |
|00000160| 69 6d 61 67 65 2f 61 76 69 66 2c 69 6d 61 67 65 |image/avif,image|
|00000170| 2f 77 65 62 70 2c 69 6d 61 67 65 2f 61 70 6e 67 |/webp,image/apng|
|00000180| 2c 69 6d 61 67 65 2f 73 76 67 2b 78 6d 6c 2c 69 |,image/svg+xml,i|
|00000190| 6d 61 67 65 2f 2a 2c 2a 2f 2a 3b 71 3d 30 2e 38 |mage/*,*/*;q=0.8|
|000001a0| 0d 0a 53 65 63 2d 46 65 74 63 68 2d 53 69 74 65 |..Sec-Fetch-Site|
|000001b0| 3a 20 73 61 6d 65 2d 6f 72 69 67 69 6e 0d 0a 53 |: same-origin..S|
|000001c0| 65 63 2d 46 65 74 63 68 2d 4d 6f 64 65 3a 20 6e |ec-Fetch-Mode: n|
|000001d0| 6f 2d 63 6f 72 73 0d 0a 53 65 63 2d 46 65 74 63 |o-cors..Sec-Fetc|
|000001e0| 68 2d 44 65 73 74 3a 20 69 6d 61 67 65 0d 0a 52 |h-Dest: image..R|
|000001f0| 65 66 65 72 65 72 3a 20 68 74 74 70 3a 2f 2f 6c |eferer: http://l|
|00000200| 6f 63 61 6c 68 6f 73 74 3a 38 30 38 30 2f 68 65 |ocalhost:8080/he|
|00000210| 6c 6c 6f 0d 0a 41 63 63 65 70 74 2d 45 6e 63 6f |llo..Accept-Enco|
|00000220| 64 69 6e 67 3a 20 67 7a 69 70 2c 20 64 65 66 6c |ding: gzip, defl|
|00000230| 61 74 65 2c 20 62 72 2c 20 7a 73 74 64 0d 0a 41 |ate, br, zstd..A|
|00000240| 63 63 65 70 74 2d 4c 61 6e 67 75 61 67 65 3a 20 |ccept-Language: |
|00000250| 7a 68 2d 43 4e 2c 7a 68 3b 71 3d 30 2e 39 2c 65 |zh-CN,zh;q=0.9,e|
|00000260| 6e 3b 71 3d 30 2e 38 2c 65 6e 2d 47 42 3b 71 3d |n;q=0.8,en-GB;q=|
|00000270| 30 2e 37 2c 65 6e 2d 55 53 3b 71 3d 30 2e 36 0d |0.7,en-US;q=0.6.|
|00000280| 0a 43 6f 6f 6b 69 65 3a 20 49 64 65 61 2d 39 66 |.Cookie: Idea-9f|
|00000290| 61 62 39 61 62 30 3d 35 32 31 66 38 65 33 38 2d |ab9ab0=521f8e38-|
|000002a0| 62 34 32 35 2d 34 63 62 36 2d 39 63 39 63 2d 32 |b425-4cb6-9c9c-2|
|000002b0| 61 65 64 31 66 38 64 32 66 38 30 3b 20 58 58 4c |aed1f8d2f80; XXL|
|000002c0| 5f 4a 4f 42 5f 4c 4f 47 49 4e 5f 49 44 45 4e 54 |_JOB_LOGIN_IDENT|
|000002d0| 49 54 59 3d 37 62 32 32 36 39 36 34 32 32 33 61 |ITY=7b226964223a|
|000002e0| 33 31 32 63 32 32 37 35 37 33 36 35 37 32 36 65 |312c22757365726e|
|000002f0| 36 31 36 64 36 35 32 32 33 61 32 32 36 31 36 34 |616d65223a226164|
|00000300| 36 64 36 39 36 65 32 32 32 63 32 32 37 30 36 31 |6d696e222c227061|
|00000310| 37 33 37 33 37 37 36 66 37 32 36 34 32 32 33 61 |7373776f7264223a|
|00000320| 32 32 36 35 33 31 33 30 36 31 36 34 36 33 33 33 |2265313061646333|
|00000330| 33 39 33 34 33 39 36 32 36 31 33 35 33 39 36 31 |3934396261353961|
|00000340| 36 32 36 32 36 35 33 35 33 36 36 35 33 30 33 35 |6262653536653035|
|00000350| 33 37 36 36 33 32 33 30 36 36 33 38 33 38 33 33 |3766323066383833|
|00000360| 36 35 32 32 32 63 32 32 37 32 36 66 36 63 36 35 |65222c22726f6c65|
|00000370| 32 32 33 61 33 31 32 63 32 32 37 30 36 35 37 32 |223a312c22706572|
|00000380| 36 64 36 39 37 33 37 33 36 39 36 66 36 65 32 32 |6d697373696f6e22|
|00000390| 33 61 36 65 37 35 36 63 36 63 37 64 0d 0a 0d 0a |3a6e756c6c7d....|
+--------+-------------------------------------------------+----------------+
00:28:10.931 [nioEventLoopGroup-3-1] INFO com.example.code.updatenetty.c6http.TestHttp - /favicon.ico
00:28:10.931 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] WRITE: 60B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d |HTTP/1.1 200 OK.|
|00000010| 0a 63 6f 6e 74 65 6e 74 2d 6c 65 6e 67 74 68 3a |.content-length:|
|00000020| 20 32 31 0d 0a 0d 0a 3c 68 31 3e 48 65 6c 6c 6f | 21....<h1>Hello|
|00000030| 2c 20 77 6f 72 6c 64 3c 2f 68 31 3e |, world</h1> |
+--------+-------------------------------------------------+----------------+
00:28:10.932 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] FLUSH
00:28:10.932 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message EmptyLastHttpContent that reached at the tail of the pipeline. Please check your pipeline configuration.
00:28:10.932 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LoggingHandler#0, HttpServerCodec#0, TestHttp$1$1#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348].
00:28:10.932 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x6b07a866, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:55348] READ COMPLETE
参考链接
Netty使用篇:半包&粘包_netty 半包粘包-CSDN博客