Search code examples
javaspring-boottcpspring-integrationserversocket

How to create a working TCP Server socket in spring boot and how to handle the incoming message?


I have tried to implement a TCP server socket with spring integration in an allready existing spring boot application, but I am facing a problem and this problem drives me crazy... The client is sending a message (a byte array) to the server and timesout. That's it. I am not receiving any exceptions from the server. It seems I have provided the wrong port or somthing but after checking the port, I am sure it is the right one.

This is my annotation based configuration class:

import home.brew.server.socket.ServerSocketHandler;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.ip.dsl.Tcp;

@Log4j2
@Configuration
@EnableIntegration
public class TcpServerSocketConfiguration {

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

    @Bean
    public IntegrationFlow server(ServerSocketHandler serverSocketHandler) {
        TcpServerConnectionFactorySpec connectionFactory = 
            Tcp.netServer(socketPort) 
              .deserializer(new CustomSerializerDeserializer())
              .serializer(new CustomSerializerDeserializer())
              .soTcpNoDelay(true);

        TcpInboundGatewaySpec inboundGateway = 
           Tcp.inboundGateway(connectionFactory);

        return IntegrationFlows
         .from(inboundGateway)
         .handle(serverSocketHandler::handleMessage)
         .get();
    }

    @Bean
    public ServerSocketHandler serverSocketHandler() {
        return new ServerSocketHandler();
    }
}

I wanted to make the receive functionality work before I try to send an answer, so that's why have a minimal configuration.

And the following class should process the received message from the server socket

import lombok.extern.log4j.Log4j2;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;

@Log4j2
public class ServerSocketHandler {

    public String handleMessage(Message<?> message, MessageHeaders messageHeaders) {
        log.info(message.getPayload());
        // TODO implement something useful to process the incoming message here...
        return message.getPayload().toString();
    } 
}

The handler method from above was never invoked even once! I have googled for some example implementations or tutorials but I haven't found anyhing what worked for me. I allready tried the implementations of these sites:

  1. https://vispud.blogspot.com/2019/03/how-to-implement-simple-echo-socket.html
  2. https://docs.spring.io/spring-integration/docs/current/reference/html/ip.html#note-nio
  3. Spring Boot TCP Client

and a bunch of sites more... but nothing helped me :-(

UPDATE 1

I have implemented a custom serializer/deserializer:

import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@Log4j2
@Data
public class CustomSerializerDeserializer implements Serializer<byte[]>, 
Deserializer<byte[]> {


@Override
public byte[] deserialize(InputStream inputStream) throws IOException {
    return inputStream.readAllBytes();
}

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

After the client have sent a message, the custom serializer is invoked but the content ist always empty. I have no idea why.... The serializer needs a lot of time to read all bytes from the stream and in the end it is empty. The procedure is repeating all the time, so I think I have build an infinty loop by accident...

UPDATE 2

I have captured the communication between Client and server socket: It looks like I am stuck in the handshake and therefore there is no payload...

Capture communication between client and server socket

So if anybody could help me out with this, I would be very thankful and if you need some more information, just let me know.

Thanks in advance!


Solution

  • How are you communicating with this server? By default the connection factory is configured to require the input to be terminated by CRLF (e.g. Telnet). You have to configure a different deserializer if your client uses something else to indicate a message end.

    Also, your method signature is incorrect; it should be:

    public String handleMessage(byte[] message, MessageHeaders messageHeaders) {
        String string = new String(message);
        System.out.println(string);
        return string.toUpperCase();
    }
    

    This works fine for me with Telnet:

    $ telnet localhost 1234
    Trying ::1...
    Connected to localhost.
    Escape character is '^]'.
    foo
    FOO
    ^]
    telnet> quit
    Connection closed.
    

    And here is a version that works with just LF (e.g. netcat):

    @Bean
    public IntegrationFlow server(ServerSocketHandler serverSocketHandler) {
        return IntegrationFlows.from(Tcp.inboundGateway(
                Tcp.netServer(1234)
                    .deserializer(TcpCodecs.lf())
                    .serializer(TcpCodecs.lf())))
                .handle(serverSocketHandler::handleMessage)
                .get();
    }
    
    $ nc localhost 1234
    foo
    FOO
    ^C