0%

java | 粘包 & 半包

说一下

  • 粘包
  • 半包

粘包

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.redisc;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;

public class Client {
static final Logger log = LoggerFactory.getLogger(Client.class);

public static void main(String[] args) throws IOException, InterruptedException {
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 socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
// 会在连接 channel 建立成功后,触发 active 事件
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 14; i++) {
ByteBuf byteBuf = ctx.alloc().buffer(16);
byteBuf.writeBytes(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
ctx.writeAndFlush(byteBuf);
}
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8000);
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("error", e);
} finally {
worker.shutdownGracefully();
}
}
}

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.redisc;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;


@Slf4j
public class Server {

public static void main(String[] args) throws IOException {
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.DEBUG));
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("error", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}

}
}
  • 开启服务端
  • 开启客户端

服务端输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
11:46:53.302 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xb6dbd17e, L:/127.0.0.1:8000 - R:/127.0.0.1:50839] READ: 126B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 01 02 03 04 05 06 07 08 09 01 02 03 04 05 06 07 |................|
|00000010| 08 09 01 02 03 04 05 06 07 08 09 01 02 03 04 05 |................|
|00000020| 06 07 08 09 01 02 03 04 05 06 07 08 09 01 02 03 |................|
|00000030| 04 05 06 07 08 09 01 02 03 04 05 06 07 08 09 01 |................|
|00000040| 02 03 04 05 06 07 08 09 01 02 03 04 05 06 07 08 |................|
|00000050| 09 01 02 03 04 05 06 07 08 09 01 02 03 04 05 06 |................|
|00000060| 07 08 09 01 02 03 04 05 06 07 08 09 01 02 03 04 |................|
|00000070| 05 06 07 08 09 01 02 03 04 05 06 07 08 09 |.............. |
+--------+-------------------------------------------------+----------------+
11:46:53.303 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded inbound message PooledUnsafeDirectByteBuf(ridx: 0, widx: 126, cap: 2048) that reached at the tail of the pipeline. Please check your pipeline configuration.
11:46:53.305 [nioEventLoopGroup-3-1] DEBUG io.netty.channel.DefaultChannelPipeline - Discarded message pipeline : [LoggingHandler#0, DefaultChannelPipeline$TailContext#0]. Channel : [id: 0xb6dbd17e, L:/127.0.0.1:8000 - R:/127.0.0.1:50839].
11:46:53.306 [nioEventLoopGroup-3-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xb6dbd17e, L:/127.0.0.1:8000 - R:/127.0.0.1:50839] READ COMPLETE

虽然客户端是

1
2
3
4
5
for (int i = 0; i < 14; i++) {
ByteBuf byteBuf = ctx.alloc().buffer(16);
byteBuf.writeBytes(new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
ctx.writeAndFlush(byteBuf);
}

但是,服务端并不是一个个接收,而是一起接收。

半包

在服务端增加一句

serverBootstrap.option(ChannelOption.SO_RCVBUF,10);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.redisc;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;


@Slf4j
public class Server {

public static void main(String[] args) throws IOException {
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();

try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_RCVBUF, 10);
serverBootstrap.group(boss, worker);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("error", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}

}
}

这句话是把缓冲区大小设置为 10

但是,在 MacBook 中输出还是上面的输出。在 windowns 可以看到半包的情况,这是因为底层的操作系统使用的缓冲区逻辑不同。

请我喝杯咖啡吧~