snp0k
snp0k

Reputation: 398

Reading TCP stream messages

I'm stuck writing a service that simply receives TCP stream messages using spring-integration. I'm using this class to send test message:

class TCPClient {
   public static void main(String args[]) throws Exception {
      Socket clientSocket = new Socket("localhost", 9999);

      clientSocket.getOutputStream().write("XYZ".getBytes());
      clientSocket.close();
   }
}

Server code, that should receive a message:

@EnableIntegration
@IntegrationComponentScan
@Configuration
public class TcpServer {

   @Bean
   public AbstractServerConnectionFactory serverCF() {
      return new TcpNetServerConnectionFactory(9999);
   }

   @Bean
   public TcpInboundGateway tcpInGate(AbstractServerConnectionFactory conFactory) {
      TcpInboundGateway inGate = new TcpInboundGateway();
      inGate.setConnectionFactory(conFactory);

      SubscribableChannel channel = new DirectChannel();

      //Planning to set custom message handler here, to process messages later
      channel.subscribe(message -> System.out.println(convertMessage(message)));

      inGate.setRequestChannel(channel);
      return inGate;
   }

   private String convertMessage(Message<?> message) {
      return message == null || message.getPayload() == null
        ? null
        : new String((byte[]) message.getPayload());
   }
}

Problem: when client code is run - server logs the following exception:

TcpNetConnection  : Read exception localhost:46924:9999:6d00ac25-b5c8-47ac-9bdd-edb6bc09fe55 IOException:Socket closed during message assembly

A am able to receive a message when I send it using telnet or when I use simple java-only tcp-server implementation. How can I configure spring-integration to be able to read message sent from client?

Upvotes: 1

Views: 1202

Answers (1)

Gary Russell
Gary Russell

Reputation: 174494

The default deserializer expects messages to be terminated with CRLF (which is what Telnet sends, and why that works).

Send "XYZ\r\n".getBytes().

Or, change the deserializer to use a ByteArrayRawDeserializer which uses the socket close to terminate the message.

See the documentation about (de)serializers here.

TCP is a streaming protocol; this means that some structure has to be provided to data transported over TCP, so the receiver can demarcate the data into discrete messages. Connection factories are configured to use (de)serializers to convert between the message payload and the bits that are sent over TCP. This is accomplished by providing a deserializer and serializer for inbound and outbound messages respectively. A number of standard (de)serializers are provided.

The ByteArrayCrlfSerializer*, converts a byte array to a stream of bytes followed by carriage return and linefeed characters (\r\n). This is the default (de)serializer and can be used with telnet as a client, for example.

...

The ByteArrayRawSerializer*, converts a byte array to a stream of bytes and adds no additional message demarcation data; with this (de)serializer, the end of a message is indicated by the client closing the socket in an orderly fashion. When using this serializer, message reception will hang until the client closes the socket, or a timeout occurs; a timeout will NOT result in a message.

Upvotes: 1

Related Questions