粘包与半包
粘包现象
粘包的问题出现是因为不知道一个用户消息的边界在哪,如果知道了边界在哪,吸收方就可以通过边界来划分出有效的用户消息。
服务端代码- public class HelloWorldServer {
- static final Logger log = LoggerFactory.getLogger(HelloWorldServer.class);
- void start() {
- NioEventLoopGroup boss = new NioEventLoopGroup(1);
- 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 ch) throws Exception {
- ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.debug("connected {}", ctx.channel());
- super.channelActive(ctx);
- }
- @Override
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
- log.debug("disconnect {}", ctx.channel());
- super.channelInactive(ctx);
- }
- });
- }
- });
- ChannelFuture channelFuture = serverBootstrap.bind(8080);
- log.debug("{} binding...", channelFuture.channel());
- channelFuture.sync();
- log.debug("{} bound...", channelFuture.channel());
- channelFuture.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- log.error("server error", e);
- } finally {
- boss.shutdownGracefully();
- worker.shutdownGracefully();
- log.debug("stoped");
- }
- }
- public static void main(String[] args) {
- new HelloWorldServer().start();
- }
- }
复制代码 客户端代码盼望发送 10 个消息,每个消息是 16 字节- public class HelloWorldClient {
- static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
- public static void main(String[] args) {
- NioEventLoopGroup worker = new NioEventLoopGroup();
- try {
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.channel(NioSocketChannel.class);
- bootstrap.group(worker);
- bootstrap.handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- log.debug("connetted...");
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- //会在连接channel建立成功后,会触发Active事件
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.debug("sending...");
- Random r = new Random();
- char c = 'a';
- for (int i = 0; i < 10; i++) {
- ByteBuf buffer = ctx.alloc().buffer();
- buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
- ctx.writeAndFlush(buffer);
- }
- }
- });
- }
- });
- ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
- channelFuture.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- log.error("client error", e);
- } finally {
- worker.shutdownGracefully();
- }
- }
- }
复制代码 服务器端的某次输出,可以看到一次就吸收了 160 个字节,而期望的是一次16字节,分 10 次吸收。这就出现了粘包现象- 08:24:46 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x81e0fda5] binding...
- 08:24:46 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x81e0fda5, L:/0:0:0:0:0:0:0:0:8080] bound...
- 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] REGISTERED
- 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] ACTIVE
- 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] c.i.n.HelloWorldServer - connected [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177]
- 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] READ: 160B
- +-------------------------------------------------+
- | 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 |................|
- |00000010| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000020| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000030| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000040| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000050| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000060| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000070| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000080| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- |00000090| 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
- +--------+-------------------------------------------------+----------------+
- 08:24:55 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x94132411, L:/127.0.0.1:8080 - R:/127.0.0.1:58177] READ COMPLETE
复制代码 半包现象
半包是指 吸收端只收到了部门数据,而非完备的数据的情况
客户端代码盼望发送 1 个消息,这个消息是 160 字节,代码改为- ByteBuf buffer = ctx.alloc().buffer();
- for (int i = 0; i < 10; i++) {
- buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
- }
- ctx.writeAndFlush(buffer);
复制代码 为现象显着,服务端修改一下吸收缓冲区,其它代码不变- serverBootstrap.option(ChannelOption.SO_RCVBUF, 10);
复制代码 服务器端的某次输出,可以看到吸收的消息被分为两节,如 第一次 20 字节,第二次 140 字节- 08:43:49 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x4d6c6a84] binding...
- 08:43:49 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0x4d6c6a84, L:/0:0:0:0:0:0:0:0:8080] bound...
- 08:44:23 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] REGISTERED
- 08:44:23 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] ACTIVE
- 08:44:23 [DEBUG] [nioEventLoopGroup-3-1] c.i.n.HelloWorldServer - connected [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221]
- 08:44:24 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] READ: 20B
- +-------------------------------------------------+
- | 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 |................|
- |00000010| 00 01 02 03 |.... |
- +--------+-------------------------------------------------+----------------+
- 08:44:24 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] READ COMPLETE
- 08:44:24 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] READ: 140B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000010| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000020| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000030| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000040| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000050| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000060| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000070| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 00 01 02 03 |................|
- |00000080| 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |............ |
- +--------+-------------------------------------------------+----------------+
- 08:44:24 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x1719abf7, L:/127.0.0.1:8080 - R:/127.0.0.1:59221] READ COMPLETE
复制代码留意:serverBootstrap.option(ChannelOption.SO_RCVBUF, 10) 影响的底层吸收缓冲区(即滑动窗口)大小,仅决定了 netty 读取的最小单位,netty 实际每次读取的一般是它的整数倍
现象分析
这里出现的粘包半包问题,并非是JavaNIO或Netty的问题,本质是TCP是流失协议,消息无边界。
粘包:
- 现象,发送 abc def,吸收 abcdef
- 原因
- 应用层:吸收方 ByteBuf 设置太大(Netty 默认 1024)
- 滑动窗口:假设发送方 256 bytes 表现一个完备报文,但由于吸收方处置惩罚不及时且窗口大小足够大,这 256 bytes 字节就会缓冲在吸收方的滑动窗口中,当滑动窗口中缓冲了多个报文就会粘包
- Nagle 算法:会造成粘包
半包
- 现象,发送 abcdef,吸收 abc def
- 原因
- 应用层:吸收方 ByteBuf 小于实际发送数据量
- 滑动窗口:假设吸收方的窗口只剩了 128 bytes,发送方的报文大小是 256 bytes,这时放不下了,只能先发送前 128 bytes,等待 ack 后才能发送剩余部门,这就造成了半包
- MSS 限制:当发送的数据凌驾 MSS 限制后,会将数据切分发送,就会造成半包
解决方案
接下来看下Netty如何解决以上问题的:
- 短链接,发一个包建立一次连接,这样连接建立到连接断开之间就是消息的边界,缺点效率太低
- 每一条消息接纳固定长度,缺点浪费空间
- 每一条消息接纳分隔符,例如 \n,缺点需要转义
- 每一条消息分为 head 和 body,head 中包罗 body 的长度
方法1:短链接(极不保举)
以解决粘包为例- public class HelloWorldClient {
- static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
- public static void main(String[] args) {
- // 分 10 次发送
- for (int i = 0; i < 10; i++) {
- send();
- }
- }
- private static void send() {
- NioEventLoopGroup worker = new NioEventLoopGroup();
- try {
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.channel(NioSocketChannel.class);
- bootstrap.group(worker);
- bootstrap.handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- log.debug("conneted...");
- ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.debug("sending...");
- ByteBuf buffer = ctx.alloc().buffer();
- buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
- ctx.writeAndFlush(buffer);
- // 发完即关
- ctx.close();
- }
- });
- }
- });
- ChannelFuture channelFuture = bootstrap.connect("localhost", 8080).sync();
- channelFuture.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- log.error("client error", e);
- } finally {
- worker.shutdownGracefully();
- }
- }
- }
复制代码 输出,略
半包用这种办法照旧欠好解决,因为吸收方的缓冲区大小是有限的
方法2:固定长度
让所有数据包长度固定(假设长度为 8 字节),服务器端参加- ch.pipeline().addLast(new FixedLengthFrameDecoder(8));
复制代码 客户端测试代码,留意, 接纳这种方法后,客户端什么时候 flush 都可以- public class HelloWorldClient {
- static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
- public static void main(String[] args) {
- NioEventLoopGroup worker = new NioEventLoopGroup();
- try {
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.channel(NioSocketChannel.class);
- bootstrap.group(worker);
- bootstrap.handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- log.debug("connetted...");
- ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.debug("sending...");
- // 发送内容随机的数据包
- Random r = new Random();
- char c = 'a';
- ByteBuf buffer = ctx.alloc().buffer();
- for (int i = 0; i < 10; i++) {
- byte[] bytes = new byte[8];
- for (int j = 0; j < r.nextInt(8); j++) {
- bytes[j] = (byte) c;
- }
- c++;
- buffer.writeBytes(bytes);
- }
- ctx.writeAndFlush(buffer);
- }
- });
- }
- });
- ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
- channelFuture.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- log.error("client error", e);
- } finally {
- worker.shutdownGracefully();
- }
- }
- }
复制代码 客户端输出- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - connetted...
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2] REGISTERED
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2] CONNECT: /192.168.0.103:9090
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] ACTIVE
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - sending...
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] WRITE: 80B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 61 61 61 61 00 00 00 00 62 00 00 00 00 00 00 00 |aaaa....b.......|
- |00000010| 63 63 00 00 00 00 00 00 64 00 00 00 00 00 00 00 |cc......d.......|
- |00000020| 00 00 00 00 00 00 00 00 66 66 66 66 00 00 00 00 |........ffff....|
- |00000030| 67 67 67 00 00 00 00 00 68 00 00 00 00 00 00 00 |ggg.....h.......|
- |00000040| 69 69 69 69 69 00 00 00 6a 6a 6a 6a 00 00 00 00 |iiiii...jjjj....|
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x3c2ef3c2, L:/192.168.0.103:53155 - R:/192.168.0.103:9090] FLUSH
复制代码 服务端输出- 12:06:51 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0xe3d9713f] binding...
- 12:06:51 [DEBUG] [main] c.i.n.HelloWorldServer - [id: 0xe3d9713f, L:/192.168.0.103:9090] bound...
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] REGISTERED
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] ACTIVE
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] c.i.n.HelloWorldServer - connected [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155]
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 61 61 61 61 00 00 00 00 |aaaa.... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 62 00 00 00 00 00 00 00 |b....... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 63 63 00 00 00 00 00 00 |cc...... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 64 00 00 00 00 00 00 00 |d....... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 00 00 00 00 00 00 00 00 |........ |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 66 66 66 66 00 00 00 00 |ffff.... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 67 67 67 00 00 00 00 00 |ggg..... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 68 00 00 00 00 00 00 00 |h....... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 69 69 69 69 69 00 00 00 |iiiii... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ: 8B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 6a 6a 6a 6a 00 00 00 00 |jjjj.... |
- +--------+-------------------------------------------------+----------------+
- 12:07:00 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0xd739f137, L:/192.168.0.103:9090 - R:/192.168.0.103:53155] READ COMPLETE
复制代码 缺点是,数据包的大小欠好把握
- 长度定的太大,浪费
- 长度定的太小,对某些数据包又显得不够
方法3:固定分隔符
服务端参加,默认以 \n 或 \r\n 作为分隔符,如果超出指定长度仍未出现分隔符,则抛出异常- ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
复制代码 客户端在每条消息之后,参加 \n 分隔符- public class HelloWorldClient {
- static final Logger log = LoggerFactory.getLogger(HelloWorldClient.class);
- public static void main(String[] args) {
- NioEventLoopGroup worker = new NioEventLoopGroup();
- try {
- Bootstrap bootstrap = new Bootstrap();
- bootstrap.channel(NioSocketChannel.class);
- bootstrap.group(worker);
- bootstrap.handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- log.debug("connetted...");
- ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- log.debug("sending...");
- Random r = new Random();
- char c = 'a';
- ByteBuf buffer = ctx.alloc().buffer();
- for (int i = 0; i < 10; i++) {
- for (int j = 1; j <= r.nextInt(16)+1; j++) {
- buffer.writeByte((byte) c);
- }
- buffer.writeByte(10);
- c++;
- }
- ctx.writeAndFlush(buffer);
- }
- });
- }
- });
- ChannelFuture channelFuture = bootstrap.connect("192.168.0.103", 9090).sync();
- channelFuture.channel().closeFuture().sync();
- } catch (InterruptedException e) {
- log.error("client error", e);
- } finally {
- worker.shutdownGracefully();
- }
- }
- }
复制代码 增长设置类和设置文件- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - connetted...
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755] REGISTERED
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755] CONNECT: /192.168.0.103:9090
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] ACTIVE
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] c.i.n.HelloWorldClient - sending...
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] WRITE: 60B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 61 0a 62 62 62 0a 63 63 63 0a 64 64 0a 65 65 65 |a.bbb.ccc.dd.eee|
- |00000010| 65 65 65 65 65 65 65 0a 66 66 0a 67 67 67 67 67 |eeeeeee.ff.ggggg|
- |00000020| 67 67 0a 68 68 68 68 0a 69 69 69 69 69 69 69 0a |gg.hhhh.iiiiiii.|
- |00000030| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 0a |jjjjjjjjjjj. |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-2-1] i.n.h.l.LoggingHandler - [id: 0x1282d755, L:/192.168.0.103:63641 - R:/192.168.0.103:9090] FLUSH
复制代码 设置文件- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] c.i.n.HelloWorldServer - connected [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641]
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 1B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 61 |a |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 3B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 62 62 62 |bbb |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 3B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 63 63 63 |ccc |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 2B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 64 64 |dd |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 10B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 65 65 65 65 65 65 65 65 65 65 |eeeeeeeeee |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 2B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 66 66 |ff |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 7B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 67 67 67 67 67 67 67 |ggggggg |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 4B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 68 68 68 68 |hhhh |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 7B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 69 69 69 69 69 69 69 |iiiiiii |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ: 11B
- +-------------------------------------------------+
- | 0 1 2 3 4 5 6 7 8 9 a b c d e f |
- +--------+-------------------------------------------------+----------------+
- |00000000| 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a 6a |jjjjjjjjjjj |
- +--------+-------------------------------------------------+----------------+
- 14:08:18 [DEBUG] [nioEventLoopGroup-3-5] i.n.h.l.LoggingHandler - [id: 0xa4b3be43, L:/192.168.0.103:9090 - R:/192.168.0.103:63641] READ COMPLETE
复制代码 修改编解码器
[code]/** * 必须和 LengthFieldBasedFrameDecoder 一起利用,确保接到的 ByteBuf 消息是完备的 */public class MessageCodecSharable extends MessageToMessageCodec { @Override public void encode(ChannelHandlerContext ctx, Message msg, List outList) throws Exception { ByteBuf out = ctx.alloc().buffer(); // 1. 4 字节的魔数 out.writeBytes(new byte[]{1, 2, 3, 4}); // 2. 1 字节的版本, out.writeByte(1); // 3. 1 字节的序列化方式 jdk 0 , json 1 out.writeByte(Config.getSerializerAlgorithm().ordinal()); // 4. 1 字节的指令类型 out.writeByte(msg.getMessageType()); // 5. 4 个字节 out.writeInt(msg.getSequenceId()); // 无意义,对齐填充 out.writeByte(0xff); // 6. 获取内容的字节数组 byte[] bytes = Config.getSerializerAlgorithm().serialize(msg); // 7. 长度 out.writeInt(bytes.length); // 8. 写入内容 out.writeBytes(bytes); outList.add(out); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { int magicNum = in.readInt(); byte version = in.readByte(); byte serializerAlgorithm = in.readByte(); // 0 或 1 byte messageType = in.readByte(); // 0,1,2... int sequenceId = in.readInt(); in.readByte(); int length = in.readInt(); byte[] bytes = new byte[length]; in.readBytes(bytes, 0, length); // 找到反序列化算法 Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlgorithm]; // 确定具体消息类型 Class, Object> options = options0(); synchronized (options) { setChannelOptions(channel, options, logger); } final Map, Object>[] currentChildOptions; final Entry |