最近在使用 Spring Boot 对接物联网设备,需要通过 TCP 协议进行通信。调研过程中发现,如果使用 Netty 框架并集成到 Spring Boot 中,配置和维护相对较为复杂。综合考虑后,最终选择了 Spring Integration 提供的 TCP/UDP 模块来实现相关功能,整体集成更加简洁,也更符合 Spring 生态的使用习惯。
第一步:
引入依赖
bash
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ip</artifactId>
</dependency>
第二步:
提供两个tcp服务端类文件,这两个类文件大家按需选择即可。
第一个类文件:
单向接收tcp客户端数据:
bash
package com.testweb.testweb.tcp.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory;
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import java.nio.charset.StandardCharsets;
@Slf4j
@Configuration
public class TcpServerConfig {
@Value("${tcp.server.port:7788}")
private int port;
/**
* 1. TCP 连接工厂(服务端)
*/
@Bean
public AbstractServerConnectionFactory serverConnectionFactory() {
TcpNioServerConnectionFactory factory =
new TcpNioServerConnectionFactory(port);
// 关键:拆包 / 粘包解决方案(行结束符)
// 按换行符 (\r\n 或 \n) 拆包,常用于文本协议
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
// 二进制协议示例:长度头 + 消息内容(常用于物联网)
// ByteArrayLengthHeaderSerializer serializer =
// new ByteArrayLengthHeaderSerializer();
factory.setSerializer(serializer);
factory.setDeserializer(serializer);
factory.setUsingDirectBuffers(true);
return factory;
}
/**
* 2. 接收同步通道
*/
@Bean
public MessageChannel tcpReceiveChannel() {
return new DirectChannel();
}
/**
* 3. TCP 入站适配器(只接收)
*/
@Bean
public TcpReceivingChannelAdapter tcpInboundAdapter(
AbstractServerConnectionFactory factory,
MessageChannel tcpReceiveChannel) {
TcpReceivingChannelAdapter adapter =
new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(factory);
adapter.setOutputChannel(tcpReceiveChannel);
return adapter;
}
/**
* 4. 业务处理器(单向接收,不能给客户端回复)
*/
@ServiceActivator(inputChannel = "tcpReceiveChannel")
public void handleMessage(Message<byte[]> message) {
String data =
new String(message.getPayload(), StandardCharsets.UTF_8);
String connectionId =
(String) message.getHeaders().get("ip_connectionId");
log.info("收到 TCP 数据: {}", data);
log.info("来自连接: {}", connectionId);
// TODO 业务逻辑处理
}
}
第二个类文件:
双向类文件:接收客户端消息并回复
bash
package com.testweb.testweb.tcp.web;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter;
import org.springframework.integration.ip.tcp.TcpSendingMessageHandler;
import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionFactory;
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import java.nio.charset.StandardCharsets;
@Slf4j
@Configuration
public class TcpServerReplyConfig {
@Value("${tcp.server.reply.port:7799}")
// 与单向服务端端口区分
private int port;
// 回复通道
@Autowired
@Qualifier("tcpReplySendChannel")
private MessageChannel tcpReplySendChannel;
/**
* 1. TCP 连接工厂(服务端)
*/
@Bean
public AbstractServerConnectionFactory replyServerConnectionFactory() {
TcpNioServerConnectionFactory factory =
new TcpNioServerConnectionFactory(port);
// 关键:拆包 / 粘包解决方案
// 使用换行符拆包(文本协议)或长度头(物联网二进制协议)
ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer();
// ByteArrayLengthHeaderSerializer serializer =
// new ByteArrayLengthHeaderSerializer();
factory.setSerializer(serializer);
factory.setDeserializer(serializer);
factory.setUsingDirectBuffers(true);
return factory;
}
/**
* 2. 接收通道
*/
@Bean
public MessageChannel tcpReplyReceiveChannel() {
return new DirectChannel();
}
/**
* 3. TCP 入站适配器(接收客户端消息)
*/
@Bean
public TcpReceivingChannelAdapter tcpReplyInboundAdapter(
AbstractServerConnectionFactory replyServerConnectionFactory,
MessageChannel tcpReplyReceiveChannel) {
TcpReceivingChannelAdapter adapter =
new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(replyServerConnectionFactory);
adapter.setOutputChannel(tcpReplyReceiveChannel);
return adapter;
}
/**
* 4. TCP 出站通道(用于回复客户端)
*/
@Bean("tcpReplySendChannel")
public MessageChannel tcpReplySendChannel() {
return new DirectChannel();
}
/**
* 5. 出站适配器(发送回复)
*/
@Bean
@ServiceActivator(inputChannel = "tcpReplySendChannel")
public TcpSendingMessageHandler tcpReplySender(
AbstractServerConnectionFactory replyServerConnectionFactory) {
TcpSendingMessageHandler sender =
new TcpSendingMessageHandler();
sender.setConnectionFactory(replyServerConnectionFactory);
return sender;
}
/**
* 6. 业务处理器:接收客户端消息并回复
*/
@ServiceActivator(inputChannel = "tcpReplyReceiveChannel")
public void handleReplyMessage(Message<byte[]> message) {
String data =
new String(message.getPayload(), StandardCharsets.UTF_8);
String connectionId =
(String) message.getHeaders().get("ip_connectionId");
log.info("收到客户端消息: {}", data);
log.info("来自连接: {}", connectionId);
// 业务逻辑处理完后发送回复
String reply =
"服务端已经收到消息,现在给客户端回复: " + data;
tcpReplySendChannel.send(
org.springframework.messaging.support.MessageBuilder
.withPayload(reply.getBytes(StandardCharsets.UTF_8))
.setHeader("ip_connectionId", connectionId)
.build()
);
}
}
这两个配置文件 大家可以按需选择。第一个类文件就是服务端只负责接收,不会给客户端反馈
第二个类文件,接收后会反馈,如果在使用中发现有需要完善的地方,也欢迎大家留言~