博主最近的项目对接了部分硬件设备,其中有的设备只支持tcp长连接方式传输数据,博主项目系统平台作为客户端发起tcp请求到设备,设备接收到请求后作为服务端保持连接并持续发送数据到系统平台。
1.依赖引入
连接使用了netty,如果项目中没有就先引入:
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.69.Final</version>
</dependency>
2.创建客户端
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
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.DelimiterBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
/**
* @author GBX
* @description tcp长连接客户端
* @date 2024/5/29 15:11
*/
@Slf4j
public class NettyTcpClient {
private final Bootstrap bootstrap;
private final EventLoopGroup group;
private Channel channel;
public NettyTcpClient(String host, int port) {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//添加自定义结束规则
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, ByteBufAllocator.DEFAULT.buffer().writeBytes("|".getBytes(StandardCharsets.UTF_8))));
//添加自定义消息处理器
pipeline.addLast(new TcpClientHandler());
}
});
try {
channel = bootstrap.connect(host, port).sync().channel();
log.info("NettyTcpClient ===》 success");
} catch (Exception e) {
log.error("NettyTcpClient-发生异常, 信息:", e);
}
}
public void close() {
if (channel != null) {
channel.close();
}
group.shutdownGracefully();
}
public static class TcpClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
//处理接收到的数据
System.out.println("Received data ===>: " + msg.toString(StandardCharsets.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}
如上代码中所示,博主使用了 “|” 作为结束符,使用结束符可以有效地解决tcp数据读取的粘包 半包问题。
3.设置配置类以进行自启动
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author GBX
* @description tcp长连接配置类
* @date 2024/5/29 15:14
*/
@Configuration
public class NettyClientConfig {
@Bean(destroyMethod = "close")
public NettyTcpClient nettyTcpClient() {
NettyTcpClient client = new NettyTcpClient("127.0.0.1", 4001);
// 启动客户端连接
return client;
}
}
4.效果测试
启动项目,发现客户端连接远程成功:
远程服务器端,这里使用的是 NetAssist 工具进行模拟(注意:该工具在springboot项目启动前已经提前启动了服务器模式进行了端口监听,可以看到项目启动后进来一个连接):
如上图所示,在8 和 10 后分别由一个结尾符,在工具上发送数据:
在springboot控制台可以看到打印的信息为两条:
工具可以在网盘下载:
链接: https://pan.baidu.com/s/1UccmnEL4VktHTHL7P_2V_g?pwd=6fcw 提取码: 6fcw