当前位置:   article > 正文

springboot 作为客户端接收服务端的 tcp 长连接数据,并实现自定义结束符,解决 粘包 半包 问题

springboot 作为客户端接收服务端的 tcp 长连接数据,并实现自定义结束符,解决 粘包 半包 问题

博主最近的项目对接了部分硬件设备,其中有的设备只支持tcp长连接方式传输数据,博主项目系统平台作为客户端发起tcp请求到设备,设备接收到请求后作为服务端保持连接并持续发送数据到系统平台。

1.依赖引入

连接使用了netty,如果项目中没有就先引入:

  1. <dependency>
  2. <groupId>io.netty</groupId>
  3. <artifactId>netty-all</artifactId>
  4. <version>4.1.69.Final</version>
  5. </dependency>

2.创建客户端

  1. import io.netty.bootstrap.Bootstrap;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.ByteBufAllocator;
  4. import io.netty.channel.*;
  5. import io.netty.channel.nio.NioEventLoopGroup;
  6. import io.netty.channel.socket.SocketChannel;
  7. import io.netty.channel.socket.nio.NioSocketChannel;
  8. import io.netty.handler.codec.DelimiterBasedFrameDecoder;
  9. import lombok.extern.slf4j.Slf4j;
  10. import java.nio.charset.StandardCharsets;
  11. /**
  12. * @author GBX
  13. * @description tcp长连接客户端
  14. * @date 2024/5/29 15:11
  15. */
  16. @Slf4j
  17. public class NettyTcpClient {
  18. private final Bootstrap bootstrap;
  19. private final EventLoopGroup group;
  20. private Channel channel;
  21. public NettyTcpClient(String host, int port) {
  22. group = new NioEventLoopGroup();
  23. bootstrap = new Bootstrap();
  24. bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
  25. @Override
  26. public void initChannel(SocketChannel ch) throws Exception {
  27. ChannelPipeline pipeline = ch.pipeline();
  28. //添加自定义结束规则
  29. pipeline.addLast(new DelimiterBasedFrameDecoder(1024, ByteBufAllocator.DEFAULT.buffer().writeBytes("|".getBytes(StandardCharsets.UTF_8))));
  30. //添加自定义消息处理器
  31. pipeline.addLast(new TcpClientHandler());
  32. }
  33. });
  34. try {
  35. channel = bootstrap.connect(host, port).sync().channel();
  36. log.info("NettyTcpClient ===》 success");
  37. } catch (Exception e) {
  38. log.error("NettyTcpClient-发生异常, 信息:", e);
  39. }
  40. }
  41. public void close() {
  42. if (channel != null) {
  43. channel.close();
  44. }
  45. group.shutdownGracefully();
  46. }
  47. public static class TcpClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
  48. @Override
  49. protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
  50. //处理接收到的数据
  51. System.out.println("Received data ===>: " + msg.toString(StandardCharsets.UTF_8));
  52. }
  53. @Override
  54. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  55. cause.printStackTrace();
  56. ctx.close();
  57. }
  58. }
  59. }

如上代码中所示,博主使用了 “|” 作为结束符,使用结束符可以有效地解决tcp数据读取的粘包 半包问题。

3.设置配置类以进行自启动

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. /**
  4. * @author GBX
  5. * @description tcp长连接配置类
  6. * @date 2024/5/29 15:14
  7. */
  8. @Configuration
  9. public class NettyClientConfig {
  10. @Bean(destroyMethod = "close")
  11. public NettyTcpClient nettyTcpClient() {
  12. NettyTcpClient client = new NettyTcpClient("127.0.0.1", 4001);
  13. // 启动客户端连接
  14. return client;
  15. }
  16. }

4.效果测试

启动项目,发现客户端连接远程成功:

远程服务器端,这里使用的是 NetAssist 工具进行模拟(注意:该工具在springboot项目启动前已经提前启动了服务器模式进行了端口监听,可以看到项目启动后进来一个连接):

如上图所示,在8 和 10 后分别由一个结尾符,在工具上发送数据:

在springboot控制台可以看到打印的信息为两条:

工具可以在网盘下载:

链接: https://pan.baidu.com/s/1UccmnEL4VktHTHL7P_2V_g?pwd=6fcw 提取码: 6fcw

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/647137
推荐阅读
相关标签
  

闽ICP备14008679号