简体   繁体   English

在一次性使用中关闭套接字连接= false spring集成TCP Server

[英]closing socket connection in single-use=false spring integration TCP Server

I know spring integration has TcpInboundGateway and ByteArrayStxEtxSerializer to handle data coming through TCP port. 我知道Spring集成具有TcpInboundGateway和ByteArrayStxEtxSerializer来处理通过TCP端口传入的数据。

ByteArrayStxEtxSerializer works great if the TCP server needs to read all the data sent from the client and then processes it. 如果TCP服务器需要读取从客户端发送的所有数据然后进行处理,则ByteArrayStxEtxSerializer可以很好地工作。 (request and response model) I am using single-use=false so that multiple requests can be processed in the same connection. (请求和响应模型)我正在使用single-use = false,以便可以在同一连接中处理多个请求。

For example if the client sends 0x02AAPL0x03 then Server can send the AAPL price. 例如,如果客户端发送0x02AAPL0x03,则服务器可以发送AAPL价格。

My TCP Server is working if the client sends 0x02AAPL0x030x02GOOG0x03. 如果客户端发送0x02AAPL0x030x02GOOG0x03,则我的TCP服务器正在工作。 It sends the price of AAPL and GOOG price. 它发送AAPL价格和GOOG价格。

Sometimes clients can send EOT (0x04). 有时客户端可以发送EOT(0x04)。 If the client sends EOT, I would like to close the socket connection. 如果客户端发送EOT,我想关闭套接字连接。

For example: Client request can be 0x02AAPL0x030x02GOOG0x03 0x020x040x03. 例如:客户请求可以是0x02AAPL0x030x02GOOG0x03 0x020x040x03。 Note EOT came in the last packet. 注意EOT是最后一个数据包。

I know ByteArrayStxEtxSerializer deserializer can be customized to read the bytes sent by the client. 我知道ByteArrayStxEtxSerializer反序列化器可以自定义以读取客户端发送的字节。

is deserializer good place to close socket connection? 解串器是关闭插座连接的好地方吗? if not, how should spring integration framework be notified to close socket connection? 如果没有,应该如何通知spring集成框架关闭套接字连接?

Please help. 请帮忙。

Here is my spring configuration: 这是我的弹簧配置:

<int-ip:tcp-connection-factory id="crLfServer"
        type="server"
        port="${availableServerSocket}"
        single-use="false"
        so-timeout="10000"
        using-nio="false" 
        serializer="connectionSerializeDeserialize"
        deserializer="connectionSerializeDeserialize"
        so-linger="2000"/>

    <bean id="connectionSerializeDeserialize" class="org.springframework.integration.ip.tcp.serializer.ByteArrayStxEtxSerializer"/>

    <int-ip:tcp-inbound-gateway id="gatewayCrLf"
        connection-factory="crLfServer"
        request-channel="serverBytes2StringChannel"
        error-channel="errorChannel"
        reply-timeout="10000"/> <!-- reply-timeout works on inbound-gateway -->

    <int:channel id="toSA" />

    <int:service-activator input-channel="toSA"
        ref="myService"
        method="prepare"/>

    <int:object-to-string-transformer id="serverBytes2String"
        input-channel="serverBytes2StringChannel"
        output-channel="toSA"/>

    <int:transformer id="errorHandler"
        input-channel="errorChannel"
        expression="payload.failedMessage.payload + ':' + payload.cause.message"/>

UPDATE: Adding throw new SoftEndOfStreamException("Stream closed") to close the stream in serializer works and I can see the CLOSED log entry in EventListener. 更新:添加引发新的SoftEndOfStreamException(“流关闭”)以关闭序列化器中的流工作,我可以在EventListener中看到CLOSED日志条目。 When the server closes the connection, I expect to receive java.io.InputStream.read() as -1 in the client. 当服务器关闭连接时,我希望在客户端中以-1形式接收java.io.InputStream.read()。 But the client is receiving the 但是客户正在收到

java.net.SocketTimeoutException: Read timed out
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
    at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93)
    at java.io.InputStreamReader.read(InputStreamReader.java:151)

is there anything else to close the connection on the server side and propagate it to client? 还有什么要关闭服务器端的连接并将其传播到客户端的吗?

I appreciate your help. 我感谢您的帮助。

Thank you 谢谢

The deserializer doesn't have access to the socket, just the input stream; 解串器无权访问套接字,只能访问输入流。 closing it would probably work, but you will likely get a lot of noise in the log. 关闭它可能会起作用,但是您可能会在日志中听到很多噪音。

The best solution is to throw a SoftEndOfStreamException ; 最好的解决方案是抛出SoftEndOfStreamException ; that signals that the socket should be closed and everything cleaned up. 表示应关闭插座并清理所有东西。

EDIT 编辑

Add a listener to detect/log the close... 添加侦听器以检测/记录关闭...

@SpringBootApplication
public class So40471456Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So40471456Application.class, args);
        Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234);
        socket.getOutputStream().write("foo\r\n".getBytes());
        socket.close();
        Thread.sleep(10000);
        context.close();
    }

    @Bean
    public EventListener eventListener() {
        return new EventListener();
    }

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

    @Bean
    public TcpReceivingChannelAdapter inbound() {
        TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
        adapter.setConnectionFactory(server());
        adapter.setOutputChannelName("foo");
        return adapter;
    }

    @ServiceActivator(inputChannel = "foo")
    public void syso(byte[] in) {
        System.out.println(new String(in));
    }

    public static class EventListener implements ApplicationListener<TcpConnectionCloseEvent> {

        private final Log logger = LogFactory.getLog(getClass());

        @Override
        public void onApplicationEvent(TcpConnectionCloseEvent event) {
            logger.info(event);
        }

    }

}

With XML, just add a <bean/> for your listener class. 使用XML,只需为侦听器类添加<bean/>

Result: 结果:

foo
2016-11-07 16:52:04.133  INFO 29536 --- [pool-1-thread-2] c.e.So40471456Application$EventListener  : TcpConnectionCloseEvent 
[source=org.springframework.integration.ip.tcp.connection.TcpNetConnection@118a7548], 
[factory=server, connectionId=localhost:50347:1234:b9fcfaa9-e92c-487f-be59-1ed7ebd9312e] 
**CLOSED**

EDIT2 编辑2

It worked as expected for me... 它按我的预期工作了...

@SpringBootApplication
public class So40471456Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So40471456Application.class, args);
        Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234);
        socket.getOutputStream().write("foo\r\n".getBytes());
        try {
            System.out.println("\n\n\n" + socket.getInputStream().read() + "\n\n\n");
            context.getBean(EventListener.class).latch.await(10, TimeUnit.SECONDS);
        }
        finally {
            socket.close();
            context.close();
        }
    }

    @Bean
    public EventListener eventListener() {
        return new EventListener();
    }

    @Bean
    public TcpNetServerConnectionFactory server() {
        TcpNetServerConnectionFactory server = new TcpNetServerConnectionFactory(1234);
        server.setDeserializer(is -> {
            throw new SoftEndOfStreamException();
        });
        return server;
    }

    @Bean
    public TcpReceivingChannelAdapter inbound() {
        TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
        adapter.setConnectionFactory(server());
        adapter.setOutputChannelName("foo");
        return adapter;
    }

    public static class EventListener implements ApplicationListener<TcpConnectionCloseEvent> {

        private final Log logger = LogFactory.getLog(getClass());

        private final CountDownLatch latch = new CountDownLatch(1);

        @Override
        public void onApplicationEvent(TcpConnectionCloseEvent event) {
            logger.info(event);
            latch.countDown();
        }

    }

}

Result: 结果:

2016-11-08 08:27:25.964  INFO 86147 --- [           main] com.example2.So40471456Application       : Started So40471456Application in 1.195 seconds (JVM running for 1.764)



-1



2016-11-08 08:27:25.972  INFO 86147 --- [pool-1-thread-2] c.e.So40471456Application$EventListener  : TcpConnectionCloseEvent [source=org.springframework.integration.ip.tcp.connection.TcpNetConnection@fee3774], [factory=server, connectionId=localhost:54984:1234:f79a6826-0336-4823-8844-67054903a094] **CLOSED**

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM