1.TCP是面向连接、面向流的,提供高可靠性服务,收发两端(客户端和服务端)都要有一 一成对的socket,因此,发送端为了将多个发送给接收端的包,更有效的发送给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端就难于分辨出完整的数据包了,因为面向流的通信是无消息保护边界的
2.由于TCP无消息保护边界,需要在接收端处理消息边界问题,也就是我们说的粘包、拆包问题
image.png
image.png
问题演示
image.png
接收方可能将数据分为多次接收,不能保证消息的准确

解决

1.使用自定义协议 + 编解码器来解决
2.关键就是要解决 服务器端每次读取数据长度的问题,这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免TCP 粘包、拆包

自定义协议包
  1. //协议包
  2. public class MessageProtocol {
  3. private int len;
  4. private byte[] content;
  5. public int getLen() {
  6. return len;
  7. }
  8. public void setLen(int len) {
  9. this.len = len;
  10. }
  11. public byte[] getContent() {
  12. return content;
  13. }
  14. public void setContent(byte[] content) {
  15. this.content = content;
  16. }
  17. }

ClientInitializer
  1. public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ChannelPipeline pipeline = ch.pipeline();
  5. pipeline.addLast(new MyMessageEncoder());
  6. pipeline.addLast(new MyClientHandler());
  7. }
  8. }

Encoder
  1. public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocol> {
  2. @Override
  3. protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception {
  4. System.out.println("MyMessageEncoder--encode");
  5. out.writeInt(msg.getLen());
  6. out.writeBytes(msg.getContent());
  7. }
  8. }

ClientHandler
  1. public class MyClientHandler extends SimpleChannelInboundHandler<MessageProtocol> {
  2. private int count;
  3. @Override
  4. protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception {
  5. }
  6. @Override
  7. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  8. //发送10条数据
  9. for (int i = 0; i < 5; i++) {
  10. String msg = "zzzzzzz";
  11. byte[] bytes = msg.getBytes(StandardCharsets.UTF_8);
  12. int length = msg.getBytes(StandardCharsets.UTF_8).length;
  13. //创建协议包
  14. MessageProtocol messageProtocol = new MessageProtocol();
  15. messageProtocol.setLen(length);
  16. messageProtocol.setContent(bytes);
  17. ctx.writeAndFlush(messageProtocol);
  18. }
  19. }
  20. }

Client
  1. public class MyClient {
  2. public static void main(String[] args) throws Exception {
  3. EventLoopGroup group = new NioEventLoopGroup();
  4. try {
  5. Bootstrap bootstrap = new Bootstrap();
  6. bootstrap.group(group).channel(NioSocketChannel.class)
  7. .handler(new MyClientInitializer()); //自定义一个初始化类
  8. ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync();
  9. channelFuture.channel().closeFuture().sync();
  10. } finally {
  11. group.shutdownGracefully();
  12. }
  13. }
  14. }

ServerInitializer
  1. public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
  2. @Override
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ch.pipeline().addLast(new MyMessageDecoder()).addLast(new MyServerHandler());
  5. }
  6. }

Decoder
  1. public class MyMessageDecoder extends ReplayingDecoder<Void> {
  2. @Override
  3. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  4. System.out.println("decode");
  5. //将得到的二进制字节码转化为messageProtocol
  6. int len = in.readInt();
  7. byte[] content = new byte[len];
  8. in.readBytes(content);
  9. //封装成messageProtocol对象,传递给下一个handler进行处理
  10. MessageProtocol messageProtocol = new MessageProtocol();
  11. messageProtocol.setLen(len);
  12. messageProtocol.setContent(content);
  13. out.add(messageProtocol);
  14. }
  15. }

ServerHandler
  1. public class MyServerHandler extends SimpleChannelInboundHandler<MessageProtocol> {
  2. private int count;
  3. @Override
  4. protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception {
  5. int len = msg.getLen();
  6. byte[] content = msg.getContent();
  7. String s = new String(content, StandardCharsets.UTF_8);
  8. System.out.println(s);
  9. System.out.println("++++++++++++++++++");
  10. System.out.println(++count);
  11. }
  12. }

Server
  1. public class MyServer {
  2. public static void main(String[] args) throws Exception{
  3. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  4. EventLoopGroup workerGroup = new NioEventLoopGroup();
  5. try {
  6. ServerBootstrap serverBootstrap = new ServerBootstrap();
  7. serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer()); //自定义一个初始化类
  8. ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
  9. channelFuture.channel().closeFuture().sync();
  10. }finally {
  11. bossGroup.shutdownGracefully();
  12. workerGroup.shutdownGracefully();
  13. }
  14. }
  15. }

image.png
发送5次就分5次接收,不会存在粘包,拆包问题