简体   繁体   中英

Spring Integration : Failed to send reply after netcat's command

My use case:

  1. Client.netcat to send tcp socket to the server
echo -n "coincoincoin" | nc -v host port
  1. Server tcp with Spring Integration receive the request from client
  2. Sever receiving the request with TcpInboundGateway, process
  3. Server send back the reply to the client through TcpInboundGateway
  4. The client receives and display the answer.

Unfortunately, the step from one to 3 is OK. The step 4 is not OK and I get a

... Failed to send reply ...

The server config's file:

@Configuration
public class ServerConfig {

    @Value("${server.port}")
    private int port;

   @Bean
    TcpNetServerConnectionFactory serverConnexion() {
        TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(port);
        factory.setSingleUse(true);
        factory.setDeserializer(new CustomSerializerDeserializer());
        factory.setSerializer(new CustomSerializerDeserializer());
       factory.setSoTimeout(10000);
        return factory;
    }

    @Bean
    TcpInboundGateway tcpGateway() {
        TcpInboundGateway gateway = new TcpInboundGateway();
        gateway.setConnectionFactory(serverConnexion());
        gateway.setRequestChannel(reqChannel());
        gateway.setReplyChannel(reqChannel());
        return gateway;
    }

    @Bean
    public MessageChannel reqChannel() {
        return new DirectChannel();
    }
}

Here is my exception:

2022-12-18 14:21:51.524 DEBUG 14816 --- [pool-3-thread-2] org.springframework.integration.channel.DirectChannel : [( - )] postSend (sent=true) on channel 'bean 'reqChannel'; defined in: 'class path resource [com/server/config/ServerConfig.class]'; from source: 'com.rss.server.config.ServerConfig.reqChannel()'', message: GenericMessage [payload=byte[40], headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@68980b5b, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@68980b5b, ip_tcp_remotePort=38567, ip_connectionId=kubernetes.docker.internal:38567:11000:e143aeae-532c-4aab-9ef1-ba7930ecc3ef, ip_localInetAddress=0.0.0.0/0.0.0.0, ip_address=127.0.0.1, id=3265835d-8088-7820-d7e5-0dd46ddab69c, ip_hostname=kubernetes.docker.internal, timestamp=1671369710212}]
2022-12-18 14:21:51.524 ERROR 14816 --- [pool-3-thread-2] org.springframework.integration.ip.tcp.TcpInboundGateway : [( - )] Failed to send reply
org.springframework.messaging.MessagingException: Send Failed; nested exception is java.net.SocketException: Socket is closed
    at org.springframework.integration.ip.tcp.connection.TcpNetConnection.send(TcpNetConnection.java:119) ~[spring-integration-ip-5.5.8.jar:5.5.8]
    at org.springframework.integration.ip.tcp.TcpInboundGateway.doOnMessage(TcpInboundGateway.java:139) [spring-integration-ip-5.5.8.jar:5.5.8]
    at org.springframework.integration.ip.tcp.TcpInboundGateway.onMessage(TcpInboundGateway.java:101) [spring-integration-ip-5.5.8.jar:5.5.8]
    at org.springframework.integration.ip.tcp.connection.TcpNetConnection.receiveAndProcessMessage(TcpNetConnection.java:224) [spring-integration-ip-5.5.8.jar:5.5.8]
    at org.springframework.integration.ip.tcp.connection.TcpNetConnection.run(TcpNetConnection.java:197) [spring-integration-ip-5.5.8.jar:5.5.8]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
    at java.lang.Thread.run(Thread.java:834) [?:?]
Caused by: java.net.SocketException: Socket is closed
    at java.net.Socket.getSendBufferSize(Socket.java:1215) ~[?:?]
    at org.springframework.integration.ip.tcp.connection.TcpNetConnection.send(TcpNetConnection.java:108) ~[spring-integration-ip-5.5.8.jar:5.5.8]
    ... 7 more

How can I solve this problem? Any help would be welcome! Thank you very much!

It looks like piping the echo into nc closes the socket before the response is sent; this works fine for me...

@Component
class ServerConfig {

    private final int port = 1234;

    @Bean
    TcpNetServerConnectionFactory serverConnexion() {
        TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(port);
        factory.setSingleUse(true);
//      factory.setDeserializer(new CustomSerializerDeserializer());
//      factory.setSerializer(new CustomSerializerDeserializer());
        factory.setSoTimeout(10000);
        return factory;
    }

    @Bean
    TcpInboundGateway tcpGateway(TcpNetServerConnectionFactory serverConnexion, MessageChannel reqChannel) {
        TcpInboundGateway gateway = new TcpInboundGateway();
        gateway.setConnectionFactory(serverConnexion);
        gateway.setRequestChannel(reqChannel);
        gateway.setReplyChannel(reqChannel);
        return gateway;
    }

    @Bean
    public MessageChannel reqChannel() {
        return new DirectChannel();
    }

}

Note that I am using the default (de)serializer (CRLF).

% nc -cv localhost 1234
Connection to localhost port 1234 [tcp/search-agent] succeeded!
foo
foo

thanks for your answer @Gary, I modify (de)serializer and this work fine. Before this, I tried to use ByteArrayLengthHeaderSerializer because my default length field is a 4 byte. Unfortunelly, I get an exception

... length 808465456 exceeds max message length: 2048

Here is the complete exeption:

2022-12-23 09:02:43.746 DEBUG 59032 --- [pool-3-thread-2] org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer : [( - )] Read 4 bytes, buffer is now at 4 of 4
2022-12-23 09:02:43.748 DEBUG 59032 --- [pool-3-thread-2] org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer : [( - )] Message length is 808465456
2022-12-23 09:02:43.754 ERROR 59032 --- [pool-3-thread-2] org.springframework.integration.ip.tcp.connection.TcpNetConnection : [( - )] Read exception kubernetes.docker.internal:2522:11000:c7c6e319-8794-4dbd-91bd-48fb8c36f43e IOException:Message length 808465456 exceeds max message length: 2048

My payload looks like: 0040UUU011234021234000020065700000000001 According to me, the message length should be 40 but the exception rising is 808465456.

My CustomSerializer:

public class ModtelSerializerDeserializer extends AbstractByteArraySerializer {
    private static final List<Integer> LV0_TOTAL_LENGTH = Arrays.asList(30, 40);
    private static final int HEADER_SIZE_INT = 4;
    private static final String ZERO_PAD = "00";
    private int headerSize = HEADER_SIZE_INT;

    public ModtelSerializerDeserializer() {
        this(HEADER_SIZE_INT);
    }

    public ModtelSerializerDeserializer(int headerSize) {
        if (headerSize != HEADER_SIZE_INT ) {
            throw new IllegalArgumentException(String.format("Header size error: actual {}, expected : 4", headerSize));
        }
    }

    public byte[] deserialize(InputStream inputStream) throws IOException {
        final int headerRead = getHearderSizeFromInputStream(inputStream);
        String lvReq = StringUtils.EMPTY;
        if (LV0_TOTAL_LENGTH.contains(headerRead)) {
            lvReq = parseData(inputStream,headerRead - HEADER_SIZE_INT);
            StringBuilder builder = new StringBuilder();
            builder.append(ZERO_PAD).append(headerRead).append(lvReq);
            LOGGER.info("Deserializing request : {}  ", builder.toString());
            lvReq = builder.toString();
        }else {
            throw new IllegalArgumentException(String.format("Message to decode isn't of the expected length, expected %d or %d, actual : %d",
                    LV0_TOTAL_LENGTH.get(0), LV0_TOTAL_LENGTH.get(1), headerSize));
        }
        return lvReq.getBytes();
    }

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

    public String parseData(InputStream inputStream, int length) throws IOException {
        StringBuilder builder = new StringBuilder();
        int bite;
        for (int i = 0; i < length; ++i) {
            bite = inputStream.read();
            this.checkClosure(bite);
            builder.append((char) bite);
        }
        return builder.toString();
    }


    public String  parseData(InputStream inputStream)
            throws IOException {
        StringBuilder textBuilder = new StringBuilder();
        try (Reader reader = new BufferedReader(new InputStreamReader
                (inputStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
            int bite = 0;
            while ((bite = reader.read()) != -1) {
                this.checkClosure(bite);
                textBuilder.append((char) bite);
            }
        }
        return textBuilder.toString();
    }

    private int getHearderSizeFromInputStream(InputStream inputStream) throws IOException {
        final String value = parseData(inputStream, HEADER_SIZE_INT);
        try
        {
            Integer.parseInt(value);
        }
        catch (NumberFormatException e)
        {
            throw new IllegalArgumentException(String.format("Header size is not integer as expected : actuel value {} ", value));
        }
        return Integer.valueOf(value.toString());
    }
    protected void checkClosure(int bite) throws IOException {
        if (bite < 0) {
            throw new IOException(String.format("Socket closed during message assembly  byte is negative {} ", bite));
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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