Spring TCP server

Let’s suppose that you need configure a TCP server in Spring / Spring Boot application.

We will make it that the server prints the client’s request on the screen, then sends a response to the client.

First, let’s include the spring-integration-ip dependency. In Maven it will look like this:

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-ip</artifactId>
    <version>5.5.3</version>
</dependency>

Like gRPC ideology, in this case we have a server configuration (server) and a service. The server configuration is responsible for all technical aspects, and the service is responsible for the business logic.

So in the TcpConfig.java we set the server parameters by defining necessary beans, including the service bean:

import your.project.TcpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.ip.tcp.TcpInboundGateway;

import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory;
import org.springframework.integration.ip.tcp.connection.TcpNetServerConnectionFactory;
import org.springframework.integration.ip.tcp.serializer.ByteArrayCrLfSerializer;
import org.springframework.messaging.MessageChannel;

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class TcpConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(TcpConfig.class);
    
    private static final int TCP_SERVER_PORT = 1111;

    @Bean
    public AbstractServerConnectionFactory tcpServer() {
        LOGGER.info("Starting TCP server with port: {}", TCP_SERVER_PORT);
        TcpNetServerConnectionFactory serverCf = new TcpNetServerConnectionFactory(TCP_SERVER_PORT);
        serverCf.setSerializer(new ByteArrayCrLfSerializer());
        serverCf.setDeserializer(new ByteArrayCrLfSerializer());
        serverCf.setSoTcpNoDelay(true);
        serverCf.setSoKeepAlive(true);
        return serverCf;
    }

    @Bean
    public TcpService tcpService() {
        return new TcpService();
    }

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

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

    @Bean
    public TcpInboundGateway tcpInGate() {
        TcpInboundGateway inGate = new TcpInboundGateway();
        inGate.setConnectionFactory(tcpServer());
        inGate.setRequestChannel(fromTcp());
        inGate.setReplyChannel(toTcp());
        return inGate;
    }
}

The service class TcpService.java in our case will look like this:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

@Component
@MessageEndpoint
public class TcpService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TcpService.class);

    @ServiceActivator(inputChannel = "fromTcp")
    public byte[] handleMessage(byte[] msg) {
        LOGGER.info("Received request: {}", new String(msg));
        return "hello_from_server".getBytes(StandardCharsets.UTF_8);
    }
}

Let’s write a simple client code that can be run from another application:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.SocketFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

public class TcpIntegrationTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(TcpIntegrationTest.class);

    private static final String HOST = "localhost";
    private static final int PORT = 1111;

    public static void main(String[] args) throws Exception {
        String DATA = "hello_from_client";

        LOGGER.info("Sending request to {}:{}", HOST, PORT);
        Socket socket = SocketFactory.getDefault().createSocket(HOST, PORT);
        socket.getOutputStream().write((DATA + "\r\n").getBytes());

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String response = reader.readLine();
        LOGGER.info("Received response: {}", response);
    }
}

Please note that we add \r\n at the end of the data. This is a terminal sequence that marks the end of the message.

If you still have any questions, feel free to ask me in the comments under this article, or write me on promark33@gmail.com.

If I saved your day, you can support me :)

One thought on “Spring TCP server

Leave a Reply

Your email address will not be published.