You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何本地Telnet可连接但外部客户端无法建立Spring Integration TCP连接?

问题

我正在开发一个Spring Boot应用,作为serviceA与serviceB两个外部客户端之间的中间件,全程采用TCP/IP Socket通信。serviceA发起通信,应用接收消息后进行处理、数据库日志记录等操作,再将消息转发至serviceB;serviceB返回响应,应用处理后再回传给serviceA。

用telnet localhost <port>测试时,能正常发起通信,消息可处理至serviceC并收到响应,但实际外部客户端serviceA尝试连接应用时,应用无任何响应。

疑问:为何应用无任何响应?注:tcpInboundFlow接收的消息为字符串格式,是否与TcpInboundGateway使用的序列化/反序列化器有关?

我采用Spring Integration TCP框架,结合Java DSL与注解配置实现功能:

  • 使用TcpInboundGateway配合serverConnectionFactory接收字符串格式消息
  • 通过路由器根据业务逻辑确定目标通道
  • 使用转换器进行后续处理
  • 通过TcpOutboundGateway将消息推送至外部客户端
  • 使用serviceActivator处理外部客户端的响应

以下是部分组件代码片段(所有通道均为directChannel实现,未展示):

@Bean
public IntegrationFlow tcpInboundFlow() {
    return IntegrationFlow.from(tcpInboundGateway())
            .handle("messageProcessingService", "processMessage")
            .route(mtiRouter())
            .get();
}   

@Bean
@Router(inputChannel = "handlerOutputChannel")
public AbstractMessageRouter mtiRouter() {
    return new AbstractMessageRouter() {
        @Override
        protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
            ISOMsg isoMsg = (ISOMsg) message.getPayload();
            try {
                String mti = isoMsg.getMTI();
                log.info("reached the router");
                return switch (mti) {
                    case "0800" -> Collections.singleton(networkManagementChannel());
                    case "0420" -> Collections.singleton(reversalChannel());
                    case "0200" -> Collections.singleton(financialRequestChannel());
                    default -> throw new TmsIsoException("Unsupported MTI: " + mti);
                };

            } catch (ISOException e) {
                log.info("failed to reach the router");
                throw new TmsIsoException("Failed to get mti from iso msg");
            }
        }
    };
}

@Bean
public IntegrationFlow financialRequestFlow() {
    return IntegrationFlow.from(financialRequestChannel())
            .transform("financialRequestProcessingService", "processFinancialRequestMessage")
            .transform("transformationService", "keyMessage")
            .handle(tcpOutboundGateway())
            .transform("responseService", "processResponseMessageForPurchase")
            .get();
}                                                                                                               
@Bean
public TcpNetServerConnectionFactory serverConnectionFactory() {
    TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(30002);

    factory.setSerializer(new ByteArrayCrLfSerializer());
    factory.setDeserializer(new ByteArrayCrLfSerializer());
//        factory.setSoTimeout(60000);
    return factory;
}

@Bean
public TcpNetClientConnectionFactory clientConnectionFactory() {
    TcpNetClientConnectionFactory factory =
            new TcpNetClientConnectionFactory("196.46.20.30", 5334);
    factory.setSerializer(customIsoMessageSerializer);
    factory.setDeserializer(customIsoMessageDeserializer);
    return factory;
}

@Bean
public TcpInboundGateway tcpInboundGateway() {
    TcpInboundGateway gateway = new TcpInboundGateway();
    gateway.setConnectionFactory(serverConnectionFactory());
    gateway.setRequestChannel(requestChannel());
    gateway.setReplyChannel(replyChannel());
 //   gateway.setErrorChannel(errorChannel());
    gateway.setReplyTimeout(60000);
    gateway.setBeanName("tcpIn");
    return gateway;
}

@Bean
public TcpOutboundGateway tcpOutboundGateway() {
    TcpOutboundGateway gateway = new TcpOutboundGateway();
    gateway.setConnectionFactory(clientConnectionFactory());
    gateway.setRemoteTimeout(60000);
    gateway.setReplyChannelName("replyChannel");
    return gateway;
}
分析与解决方案

从现象和代码来看,核心问题大概率出在序列化/反序列化器不匹配以及消息流转的一致性上,具体拆解如下:

1. 序列化器不匹配是核心嫌疑

你用telnet测试正常,是因为telnet发送的消息默认会以CRLF(回车换行)作为结束符,正好匹配serverConnectionFactory里配置的ByteArrayCrLfSerializer。但外部客户端serviceA发送的消息,很可能没有遵循CRLF的结束规则,或者使用了其他序列化方式,导致服务端的ByteArrayCrLfSerializer无法正确识别消息边界,一直处于等待消息完整的状态,所以应用没有任何响应。

另外注意到,你的messageProcessingService.processMessage方法会将字符串消息转换成ISOMsg对象,但如果serviceA发送的原始消息格式(比如ISO 8583的二进制格式)和ByteArrayCrLfSerializer的字符串解析逻辑不兼容,也会导致后续路由逻辑无法触发,进而没有响应。

2. 消息流转的通道配置存在矛盾

  • 你的tcpInboundGateway指定了requestChannel和replyChannel,但tcpInboundFlow却是直接从tcpInboundGateway开始构建的。这会导致消息同时流向两个地方:一个是requestChannel,另一个是tcpInboundFlow的处理链,可能引发消息丢失或流转混乱。
  • 路由器mtiRouter的inputChannel是handlerOutputChannel,但tcpInboundFlow里的handle方法之后直接调用route(mtiRouter()),这里需要确认messageProcessingService.processMessage的输出是否正确发送到了handlerOutputChannel,否则路由器无法接收到消息,后续流程无法执行。

3. 具体修复建议

(1)统一序列化/反序列化逻辑

  • 确认serviceA的消息格式:如果是ISO 8583二进制消息,需要替换serverConnectionFactory的序列化器为你自定义的customIsoMessageSerializer和customIsoMessageDeserializer,和客户端连接工厂保持一致,而不是使用ByteArrayCrLfSerializer。
  • 如果serviceA确实发送的是带CRLF的字符串,需要在客户端侧确认消息结尾是否正确添加了\r\n,或者在服务端调整序列化器的结束符配置。

(2)修正消息流转通道配置

  • 移除tcpInboundGateway的requestChannel和replyChannel配置,因为tcpInboundFlow已经通过from(tcpInboundGateway())接管了消息处理,重复配置会导致消息分流。
  • 确保messageProcessingService.processMessage的输出通道是handlerOutputChannel,或者直接在tcpInboundFlow里明确指定输出通道,比如:
.handle("messageProcessingService", "processMessage")
.channel("handlerOutputChannel")
.route(mtiRouter())

(3)添加错误日志与调试

  • 启用tcpInboundGateway的errorChannel,并编写对应的错误处理逻辑,打印详细的异常信息,方便定位消息处理过程中的问题。
  • 在messageProcessingService.processMessage方法中添加日志,确认是否接收到了serviceA的消息,以及消息内容是否符合预期。

(4)检查网络与防火墙配置

  • 确认serviceA所在网络能正常访问应用的30002端口,没有防火墙或安全组拦截。
  • 用netstat -an命令确认应用的30002端口处于监听状态。

内容的提问来源于stack exchange,提问作者serethewind

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:07:07