TCP粘包拆包场景

时间:2024-12-24 20:38:02

TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输的时候,在网络中的byte其实就像是河水,TCP就像一个搬运工,将这流水从一端转送到另一端,这时又分两种情况:

1)如果客户端的每次制造的水比较多,也就是我们常说的客户端给的包比较大,TCP这个搬运工就会分多次去搬运。

2)如果客户端每次制造的水比较少的话,TCP可能会等客户端多次生产之后,把所有的水一起再运输到另一端

上述第一种情况,就是需要我们进行粘包,在另一端接收的时候,需要把多次获取的结果粘在一起,变成我们可以理解的信息,第二种情况,我们在另一端接收的时候,就必须进行拆包处理,因为每次接收的信息,可能是另一个远程端多次发送的包,被TCP粘在一起的

我们进行上述两种情况给出具体的场景:

1)单次发送的包内容过多的情况,拆包的现象:

我们先写客户端的bootstrap:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.ChannelPipeline;
  7. import io.netty.channel.EventLoopGroup;
  8. import io.netty.channel.nio.NioEventLoopGroup;
  9. import io.netty.channel.socket.SocketChannel;
  10. import io.netty.channel.socket.nio.NioSocketChannel;
  11. import io.netty.handler.codec.LineBasedFrameDecoder;
  12. import io.netty.handler.codec.string.StringDecoder;
  13. public class BaseClient {
  14. static final String HOST = System.getProperty("host", "127.0.0.1");
  15. static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
  16. static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
  17. public static void main(String[] args) throws Exception {
  18. EventLoopGroup group = new NioEventLoopGroup();
  19. try {
  20. Bootstrap b = new Bootstrap();
  21. b.group(group)
  22. .channel(NioSocketChannel.class)
  23. .option(ChannelOption.TCP_NODELAY,true)
  24. .handler(new ChannelInitializer<SocketChannel>() {
  25. @Override
  26. public void initChannel(SocketChannel ch) throws Exception {
  27. ChannelPipeline p = ch.pipeline();
  28. //                     p.addLast(new LineBasedFrameDecoder(1024));
  29. p.addLast(new StringDecoder());
  30. p.addLast(new BaseClientHandler());
  31. }
  32. });
  33. ChannelFuture future = b.connect(HOST, PORT).sync();
  34. future.channel().writeAndFlush("Hello Netty Server ,I am a common client");
  35. future.channel().closeFuture().sync();
  36. } finally {
  37. group.shutdownGracefully();
  38. }
  39. }
  40. }

客户端的handler:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. public class BaseClientHandler extends ChannelInboundHandlerAdapter{
  7. private byte[] req;
  8. private int counter;
  9. public BaseClientHandler() {
  10. //        req = ("BazingaLyncc is learner" + System.getProperty("line.separator"))
  11. //            .getBytes();
  12. req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  13. + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  14. + "of configuring and connecting all of Netty’s components to bring your learned about threading models in ge"
  15. + "neral and Netty’s threading model in particular, whose performance and consistency advantages we discuss"
  16. + "ed in detail In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. Hi"
  17. + "s book will give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the"
  18. + " process of configuring and connecting all of Netty’s components to bring your learned about threading "
  19. + "models in general and Netty’s threading model in particular, whose performance and consistency advantag"
  20. + "es we discussed in detailIn this chapter you general, we recommend Java Concurrency in Practice by Bri"
  21. + "an Goetz. His book will give We’ve reached an exciting point—in the next chapter;the counter is: 1 2222"
  22. + "sdsa ddasd asdsadas dsadasdas").getBytes();
  23. }
  24. @Override
  25. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  26. ByteBuf message = null;
  27. //        for (int i = 0; i < 100; i++) {
  28. //            message = Unpooled.buffer(req.length);
  29. //            message.writeBytes(req);
  30. //            ctx.writeAndFlush(message);
  31. //        }
  32. message = Unpooled.buffer(req.length);
  33. message.writeBytes(req);
  34. ctx.writeAndFlush(message);
  35. message = Unpooled.buffer(req.length);
  36. message.writeBytes(req);
  37. ctx.writeAndFlush(message);
  38. }
  39. @Override
  40. public void channelRead(ChannelHandlerContext ctx, Object msg)
  41. throws Exception {
  42. String buf = (String) msg;
  43. System.out.println("Now is : " + buf + " ; the counter is : "+ ++counter);
  44. }
  45. @Override
  46. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  47. ctx.close();
  48. }
  49. }

服务端的serverBootstrap:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. import io.netty.handler.codec.string.StringDecoder;
  11. import java.net.InetSocketAddress;
  12. public class BaseServer {
  13. private int port;
  14. public BaseServer(int port) {
  15. this.port = port;
  16. }
  17. public void start(){
  18. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  19. EventLoopGroup workerGroup = new NioEventLoopGroup();
  20. try {
  21. ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
  22. .childHandler(new ChannelInitializer<SocketChannel>() {
  23. protected void initChannel(SocketChannel ch) throws Exception {
  24. //                            ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
  25. ch.pipeline().addLast(new StringDecoder());
  26. ch.pipeline().addLast(new BaseServerHandler());
  27. };
  28. }).option(ChannelOption.SO_BACKLOG, 128)
  29. .childOption(ChannelOption.SO_KEEPALIVE, true);
  30. // 绑定端口,开始接收进来的连接
  31. ChannelFuture future = sbs.bind(port).sync();
  32. System.out.println("Server start listen at " + port );
  33. future.channel().closeFuture().sync();
  34. } catch (Exception e) {
  35. bossGroup.shutdownGracefully();
  36. workerGroup.shutdownGracefully();
  37. }
  38. }
  39. public static void main(String[] args) throws Exception {
  40. int port;
  41. if (args.length > 0) {
  42. port = Integer.parseInt(args[0]);
  43. } else {
  44. port = 8080;
  45. }
  46. new BaseServer(port).start();
  47. }
  48. }

服务端的handler:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. public class BaseServerHandler extends ChannelInboundHandlerAdapter{
  5. private int counter;
  6. @Override
  7. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  8. String body = (String)msg;
  9. System.out.println("server receive order : " + body + ";the counter is: " + ++counter);
  10. }
  11. @Override
  12. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  13. cause.printStackTrace();
  14. ctx.close();
  15. }
  16. }

照例,我们先运行服务器端:

TCP粘包拆包场景

我们再运行客户端,客户端启动后,我们再看看服务器端的控制台打印输出:

TCP粘包拆包场景

我们可以看到服务器端分三次接收到了客户端两次发送的那段很长的信息

2)单次发送的包内容过多的情况,粘包的现象:

客户端和服务端的bootstrap不改变,我们修改一下,客户端发送信息的channelActive的代码:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. public class BaseClientHandler extends ChannelInboundHandlerAdapter{
  7. private byte[] req;
  8. private int counter;
  9. public BaseClientHandler() {
  10. req = ("BazingaLyncc is learner").getBytes();
  11. //        req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  12. //                + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  13. //                + "of configuring and connecting all of Netty’s components to bring your learned about threading models in ge"
  14. //                + "neral and Netty’s threading model in particular, whose performance and consistency advantages we discuss"
  15. //                + "ed in detail In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. Hi"
  16. //                + "s book will give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the"
  17. //                + " process of configuring and connecting all of Netty’s components to bring your learned about threading "
  18. //                + "models in general and Netty’s threading model in particular, whose performance and consistency advantag"
  19. //                + "es we discussed in detailIn this chapter you general, we recommend Java Concurrency in Practice by Bri"
  20. //                + "an Goetz. His book will give We’ve reached an exciting point—in the next chapter;the counter is: 1 2222"
  21. //                + "sdsa ddasd asdsadas dsadasdas").getBytes();
  22. }
  23. @Override
  24. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  25. ByteBuf message = null;
  26. for (int i = 0; i < 100; i++) {
  27. message = Unpooled.buffer(req.length);
  28. message.writeBytes(req);
  29. ctx.writeAndFlush(message);
  30. }
  31. //        message = Unpooled.buffer(req.length);
  32. //        message.writeBytes(req);
  33. //        ctx.writeAndFlush(message);
  34. //        message = Unpooled.buffer(req.length);
  35. //        message.writeBytes(req);
  36. //        ctx.writeAndFlush(message);
  37. }
  38. @Override
  39. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  40. ctx.close();
  41. }
  42. }

我们再次启动服务器端:

TCP粘包拆包场景

启动客户端后,依旧看服务器端的控制台:

TCP粘包拆包场景

可以看出,客户端发送100次的信息,被服务器端分三次就接收了,这就发生了粘包的现象

以上就是典型的粘包和拆包的场景