TCP粘包与拆包解决方案

如何解决TCP粘包与拆包_解决方案

实现发送几次接收几次,回复几次

Client

public class Client {
private final String host;
private final int port;

public Client(String host, int port) {
this.host = host;
this.port = port;
}

public void run() {
//客户端只需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();

try {
//创建客户端启动对象,配置参数
Bootstrap bootstrap = new Bootstrap();

//用链式编程进行设置
bootstrap.group(group)//设置事件循环组
.channel(NioSocketChannel.class)//设置客户端通道实现
.handler(new ClientInitializer());//给workGroup的EventLoop对应的管道设置处理器

//启动客户端,绑定端口,连接服务端
ChannelFuture future = bootstrap.connect(host, port).sync();

//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}

public static void main(String[] args) throws InterruptedException {
new Client("127.0.0.1", 666).run();
}
}

ClientHandler

public class ClientHandler extends SimpleChannelInboundHandler<Protocol> {
private int count;

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 10; i++) {
String msg = "客户端发送第" + i + "次 ";
byte[] content = msg.getBytes(Charset.forName("utf-8"));
int len = msg.getBytes(Charset.forName("utf-8")).length;
//创建协议包
Protocol protocol = new Protocol();
protocol.setLen(len);
protocol.setBytes(content);
ctx.writeAndFlush(protocol);
}
}

@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Protocol protocol) throws Exception {
//接收到数据,并处理
int len = protocol.getLen();
byte[] bytes = protocol.getBytes();

System.out.println("客户端接收到信息如下");
System.out.println("长度:" + len);
System.out.println("内容:" + new String(bytes, Charset.forName("utf-8")));
System.out.println("客户端接收到消息包数量:" + (++this.count));
}

//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//cause.printStackTrace();
System.out.println(cause.getMessage());
ctx.close();
}
}

ClientInitializer

public class ClientInitializer extends ChannelInitializer<SocketChannel> {
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("decoder",new MessageDecoder())//解码器
.addLast("encoder",new MessageEncoder())//编码器
.addLast("ClientHandler", new ClientHandler());//添加自定义Handler
}
}

MessageDecoder

public class MessageDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
System.out.println("\n\n" + "MessageDecoder被调用几次");
//将得到二进制字节码-> Protocol数据包
int len = byteBuf.readInt();
byte[] bytes = new byte[len];
byteBuf.readBytes(bytes);
//封装成Protocol对象放入集合,传递给handler
Protocol protocol = new Protocol();
protocol.setLen(len);
protocol.setBytes(bytes);
list.add(protocol);
}
}

MessageEncoder

public class MessageEncoder extends MessageToByteEncoder<Protocol> {
@Override
protected void encode(ChannelHandlerContext ctx, Protocol protocol, ByteBuf byteBuf) throws Exception {
System.out.println("MessageEncoder被调用几次");
byteBuf.writeInt(protocol.getLen());
byteBuf.writeBytes(protocol.getBytes());
}
}

Protocol

//协议包
public class Protocol {
private int len;
private byte[] bytes;

public int getLen() {
return len;
}

public void setLen(int len) {
this.len = len;
}

public byte[] getBytes() {
return bytes;
}

public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
}

Server

public class Server {
private final int port;

public Server(int port) {
this.port = port;
}

public void run() {
//创建两个事件循环组,bossGroup只处理连接请求,workGroup处理客户端业务处理,交给bossGroup
//两个都是无线循环
//默认CPU核*2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();

try {
//创建服务端启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();

//用链式编程进行设置
bootstrap.group(bossGroup, workGroup)//设置两个线程组
.channel(NioServerSocketChannel.class)//使用NioSocketChannel作为服务器通道实现
.option(ChannelOption.SO_BACKLOG, 128)//设置线程队列个数
.childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
.childHandler(new ServerInitializer());//给workGroup的EventLoop对应的管道设置处理器

//启动服务器,绑定端口,生成ChannelFuture对象
ChannelFuture future = bootstrap.bind(port).sync();

//对关闭通道进行监听(非阻塞监听,异步模型)
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}

public static void main(String[] args) throws InterruptedException {
new Server(666).run();
}
}

ServerHandler

public class ServerHandler extends SimpleChannelInboundHandler<Protocol> {
private int count;

@Override
protected void channelRead0(ChannelHandlerContext ctx, Protocol protocol) throws Exception {
//接收到数据,并处理
int len = protocol.getLen();
byte[] bytes = protocol.getBytes();

System.out.println("服务器接收到信息如下");
System.out.println("长度:" + len);
System.out.println("内容:" + new String(bytes, Charset.forName("utf-8")));
System.out.println("服务器接收到消息包数量:" + (++this.count));

//回复消息
String str = UUID.randomUUID().toString();
int responseLen = str.getBytes("utf-8").length;
byte[] responseBytes = str.getBytes("utf-8");
//构建一个协议包
Protocol responseProtocol = new Protocol();
responseProtocol.setLen(responseLen);
responseProtocol.setBytes(responseBytes);
ctx.writeAndFlush(responseProtocol);
}

//处理异常,关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println(cause.getMessage());
ctx.close();
}
}

ServerInitializer

public class ServerInitializer extends ChannelInitializer<SocketChannel> {
//给pipeline设置处理器
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("decoder",new MessageDecoder())//解码器
.addLast("encoder",new MessageEncoder())//编码器
.addLast("ServerHandler", new ServerHandler());//添加自定义Handler
}
}