Spring integration TCP/IP close connection problem

徘徊边缘 提交于 2021-01-01 09:56:14

问题


I use spring integration as a Full Duplex communication system's gateway module. that flow is client app <--> spring-integration-ip-module (siid) <--> server app The problem is when client app closed, ssid can't closed connection with server app side? here is my code

    // siid connet to client

@Bean
public TcpNetServerConnectionFactory server(){
    TcpNetServerConnectionFactory server=new TcpNetServerConnectionFactory(1234);
    server.setMapper(new TcpSerMapper()); // use 'mapper' attribute in XML
    MySerializer mySeri=new MySerializer();
    server.setDeserializer(mySeri);
    server.setSerializer(mySeri);
    return server;
    }

// inboundGateway, inChannel as reqeustChannel
@Bean
public TcpInboundGateway inGate(){
    TcpInboundGateway inGate=new TcpInboundGateway();
    inGate.setConnectionFactory(server());
    inGate.setRequestChannelName("inChannel");
    inGate.setReplyChannelName("outputChannel");
    return inGate;
    }

// serviceActivator to get inChannel's payload msg and send though a gateway.
@ServiceActivator(inputChannel = "inChannel")
public  byte[]doClientForward(Message<?> msg){
    byte[]msgPayload=(byte[])(msg.getPayload());
    byte[]sendResult=null;
    ToTCP toTcp=(ToTCP)contextBean.get("toTcpBean"); // ToTCP is a gateway
    sendResult=toTcp.sends((msgPayload),"localhost",7779);
    QueueChannel outputChannel=(QueueChannel)contextBean.get("outputChannel");
    return sendResult;
    }

public static class DynamicSerSeri extends AbstractPooledBufferByteArraySerializer {
protected byte[] doDeserialize(InputStream inputStream, byte[] buffer) throws IOException {
    byte[] bytes = this.copyBuffer(inputStream, buffer);
    return bytes;
}

public void serialize(byte[] object, OutputStream outputStream) throws IOException {
    outputStream.write(object);
}

public byte[] copyBuffer(InputStream inputStream, byte[] buffer) throws IOException {

    int n = 0;
    int bite = 0;
    try {
        while (true) {
            bite = inputStream.read(); // blocked here
            this.setMaxMessageSize(inputStream.available() + 1);
            buffer = new byte[inputStream.available() + 1];
            if (bite < 0 && n == 0) {
                throw new SoftEndOfStreamException("Stream closed between payloads");
            }
            checkClosure(bite);
            buffer[n++] = (byte) bite;
            if (bite == -1) {
                break;

            }
            if (n == this.maxMessageSize) {
                break;
            }
        }
        return buffer;
    } catch (SoftEndOfStreamException e) {
        throw e; // I was stuck here. when client closed, cf can't receive this exception and send close singnal to server side
    } catch (IOException e) {
        publishEvent(e, buffer, n);
        throw e;
    } catch (RuntimeException e) {
        publishEvent(e, buffer, n);
        throw e;
    }
}

}


@MessagingGateway()
public interface ToTCP {
@Gateway(requestChannel = "toTcp.input", replyChannel = "outputChannel")
public byte[] sends(byte[] data, @Header("host") String host, @Header("port") int port);
}

@Bean
public IntegrationFlow toTcp() {
    return f -> f.route(new ClientTcpRouter());
}

// I am not sure I understand IntegrationFlowContext,but it works
public static class ClientTcpRouter extends AbstractMessageRouter {
@Autowired
private IntegrationFlowContext flowContext;

@Override
protected synchronized Collection<MessageChannel> determineTargetChannels(Message<?> message) {
    // connection to server side.
    TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port); //?? this connection factory does's closed when inGate's connection factory throw SoftEndOfStreamException
    TcpOutboundGateway handler = new TcpOutboundGateway();
    handler.setConnectionFactory(cf);
    cf.setDeserializer(new DynamicSerSeri());
    cf.setSerializer(new DynamicSerSeri());
    IntegrationFlow flow = f -> f.handle(handler);
    IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
            this.flowContext.registration(flow)
                    .addBean(cf)
                    .id(hostPort + ".flow")
                    .register();
    MessageChannel inputChannel = flowRegistration.getInputChannel();
    this.subFlows.put(hostPort, inputChannel);
    return inputChannel;
}
}

TcpInboundGateway get the connection from client into the inputChannel, and I use a serviceActivator to get inputChannel's payload and send to server side by a TcpOutboundGateway which has a connection factory with server side. when the client closed the connection with spring-integration-ip-module, TcpInboundGateway can get the exception in SoftEndOfStreamException, but I don't known how to closed TcpOutboundGateway's connection to the server side.


回答1:


Use an ApplicationListener bean or @EventListener method to listen for TCP Events.

When you first open an outbound connection, you will get a TcpConnectionOpenEvent. It is published on (and will be receive on) the calling thread by default. You can associate the outbound connection id with the inbound.

Listen for TcpConnectionCloseEvent from the inbound connection factory; you can then close the outbound connection using its connectionId.

outboundFactory.closeConnection(connectionId);

EDIT

Since you are using a TcpNetServerConnectionFactory, you can use a ThreadAffinityClientConnectionFactory which will automatically associate the outgoing connection with the incoming connection.

When you get the event for the incoming connection close, it will be on the same thread, so you can simply call releaseConnection() on that thread and the outgoing connection will close.

Here is an example

@SpringBootApplication
public class So55207274Application {

    public static void main(String[] args) {
        SpringApplication.run(So55207274Application.class, args);
    }

    @Bean
    public IntegrationFlow flow() {
        return IntegrationFlows.from(Tcp.inboundGateway(server()))
                .log()
                .handle(Tcp.outboundGateway(threadBoundClient()))
                .get();
    }

    @Bean
    public TcpNetServerConnectionFactory server() {
        return new TcpNetServerConnectionFactory(1234);
    }

    @Bean
    public ThreadAffinityClientConnectionFactory threadBoundClient() {
        return new ThreadAffinityClientConnectionFactory(client());
    }

    public TcpNetClientConnectionFactory client() {
        TcpNetClientConnectionFactory client = new TcpNetClientConnectionFactory("localhost", 1235);
        client.setSingleUse(true);
        return client;
    }

    @EventListener
    public void listen(TcpConnectionCloseEvent event) {
        if (event.getConnectionFactoryName().equals("server")) {
            try {
                threadBoundClient().releaseConnection();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(event);
    }

    // Test server

    @Bean
    public IntegrationFlow test() {
        return IntegrationFlows.from(Tcp.inboundGateway(Tcp.netServer(1235)))
                .transform(Transformers.objectToString())
                .<String, String>transform(p -> p.toUpperCase())
                .get();
    }

}


来源:https://stackoverflow.com/questions/55207274/spring-integration-tcp-ip-close-connection-problem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!