簡體   English   中英

Spring集成TCP / IP關閉連接問題

[英]Spring integration TCP/IP close connection problem

我使用spring集成作為全雙工通信系統的網關模塊。 該流程是客戶端應用程序< - > spring-integration-ip-module(siid)< - >服務器應用程序問題是當客戶端應用程序關閉時,ssid無法與服務器應用程序端關閉連接? 這是我的代碼

    // 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獲取從客戶端到inputChannel的連接,我使用serviceActivator獲取inputChannel的有效負載,並通過TcpOutboundGateway發送到服務器端,TcpOutboundGateway與服務器端有連接工廠。 當客戶端使用spring-integration-ip-module關閉連接時,TcpInboundGateway可以在SoftEndOfStreamException中獲取異常,但我不知道如何關閉TcpOutboundGateway與服務器端的連接。

使用ApplicationListener bean或@EventListener方法偵聽TCP事件

首次打開出站連接時,您將獲得TcpConnectionOpenEvent 它默認發布在(並將在接收)調用線程上。 您可以將出站連接ID與入站關聯。

從入站連接工廠中偵聽TcpConnectionCloseEvent ; 然后,您可以使用其connectionId關閉出站connectionId

outboundFactory.closeConnection(connectionId);

編輯

由於您使用的是TcpNetServerConnectionFactory ,因此可以使用ThreadAffinityClientConnectionFactory ,它將自動將傳出連接與傳入連接相關聯。

當您獲得傳入連接的事件關閉時,它將位於同一個線程上,因此您只需在該線程上調用releaseConnection() ,即可關閉傳出連接。

這是一個例子

@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();
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM