Building TCP and HTTP Clients and Servers with Vert.x

Vert.x provides a straightforward way to implement non-blocking TCP and HTTP clients and servers.

TCP Server Implementation

Basic Server Creation

Instantiate a TCP server with default settings:

NetServer tcpServer = vertx.createNetServer();

Server Configuration

Customize server behavior by passing a NetServerOptions object:

NetServerOptions srvOptions = new NetServerOptions().setPort(4321);
NetServer tcpServer = vertx.createNetServer(srvOptions);

Starting the Server

Invoke the listen method to start accepting connections. Configuration from NetServerOptions applies unless overridden:

NetServer tcpServer = vertx.createNetServer();
tcpServer.listen();

Override host and port directly in the listen call:

NetServer tcpServer = vertx.createNetServer();
tcpServer.listen(1234, "localhost");

The default host is 0.0.0.0 (binds to all available network interfaces). Specifying port 0 instructs the server to locate a free local port automatically.

Since binding is asynchronous, the server might not be ready immediately after the listen method returns. Attach a handler to get notified when the server is actually listening:

NetServer tcpServer = vertx.createNetServer();
tcpServer.listen(1234, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed to bind!");
  }
});

Detecting the Bound Port

When using port 0, retrieve the actual port the server bound to using actualPort:

NetServer tcpServer = vertx.createNetServer();
tcpServer.listen(0, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening on actual port: " + tcpServer.actualPort());
  } else {
    System.out.println("Failed to bind!");
  }
});

Handling Incoming Connections

Set a connectHandler to process new connections. The handler receives a NetSocket instance:

NetServer tcpServer = vertx.createNetServer();
tcpServer.connectHandler(socket -> {
  // Process the connection here
});

Reading Data

Attach a handler to the socket to receive data as Buffer objects:

NetServer tcpServer = vertx.createNetServer();
tcpServer.connectHandler(socket -> {
  socket.handler(buffer -> {
    System.out.println("I received some bytes: " + buffer.length());
  });
});

Writing Data

Send data to the socket using the write method. Operation are asynchronous:

Buffer dataBuffer = Buffer.buffer().appendFloat(12.34f).appendInt(123);
socket.write(dataBuffer);

// Write a string in UTF-8
socket.write("some data");

// Write a string with specific encoding
socket.write("some data", "UTF-16");

Socket Lifecycle Events

Close Handler:

socket.closeHandler(v -> {
  System.out.println("The socket has been closed");
});

Exception Handler:

socket.exceptionHandler(e -> {
  System.err.println("Socket error: " + e.getMessage());
});

Event Bus Integration

Each socket automatically registers an Event Bus handler. Sending buffers to this address writes them to the socket, enabling communication across verticles or Vert.x instances.

Retrieve the address using writeHandlerID.

Address Information

  • localAddress(): Returns the server-side socket address.
  • remoteAddress(): Returns the client-side socket address.

Sending Files

Efficiently transfer files or classpath resources using sendFile. This leverages OS-level kernel operations where supported:

socket.sendFile("myfile.dat");

Socket Streaming

NetSocket implements ReadStream and WriteStream, allowing integration with other streams via pumps.

Upgrading to SSL/TLS

Upgrade an existing non-secure connection using upgradeToSsl. Ensure the server or client is configured for SSL/TLS first.

Shutting Down the Server

Call close to shut down the server, releasing resources and closing connections. The operation is asynchronous:

server.close(res -> {
  if (res.succeeded()) {
    System.out.println("Server is now closed");
  } else {
    System.out.println("close failed");
  }
});

Automatic Cleanup in Verticles

TCP servers and clients created inside a verticle are automatically closed when the verticle is undeployed.

Scaling TCP Servers

Since a server instance is bound to a single event loop, deploy multiple instances to utilize multiple cores. Vert.x handles port sharing internally using a round-robin strategy.

Deploy programmatically:

for (int i = 0; i < 10; i++) {
  NetServer srv = vertx.createNetServer();
  srv.connectHandler(socket -> {
    socket.handler(buffer -> {
      socket.write(buffer); // Echo back
    });
  });
  srv.listen(1234, "localhost");
}

Or deploy verticle instances:

DeploymentOptions deployOpts = new DeploymentOptions().setInstances(10);
vertx.deployVerticle("com.mycompany.MyVerticle", deployOpts);

TCP Client Implementation

Basic Client Creation

NetClient tcpClient = vertx.createNetClient();

Client Configuration

NetClientOptions cliOptions = new NetClientOptions().setConnectTimeout(10000);
NetClient tcpClient = vertx.createNetClient(cliOptions);

Establishing Connections

NetClientOptions cliOptions = new NetClientOptions().setConnectTimeout(10000);
NetClient tcpClient = vertx.createNetClient(cliOptions);
tcpClient.connect(4321, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Connected!");
    NetSocket socket = res.result();
  } else {
    System.out.println("Failed to connect: " + res.cause().getMessage());
  }
});

Reconnection

Configure automatic reconnection for the initial connection attempt:

NetClientOptions cliOptions = new NetClientOptions()
  .setReconnectAttempts(10)
  .setReconnectInterval(500);
NetClient tcpClient = vertx.createNetClient(cliOptions);

Note: Reconnection only applies to the initial connection, not failures after a successful connect.

Network Activity Logging

Enable debugging logs via Netty (io.netty.handler.logging.LoggingHandler at DEBUG level):

// Server
NetServerOptions srvOpts = new NetServerOptions().setLogActivity(true);
NetServer tcpServer = vertx.createNetServer(srvOpts);

// Client
NetClientOptions cliOpts = new NetClientOptions().setLogActivity(true);
NetClient tcpClient = vertx.createNetClient(cliOpts);

Warning: This is a debugging feature, not intended for production.

SSL/TLS Configuration

Server-Side SSL

Enable SSL and configure keys/certificates.

Using JKS:

NetServerOptions srvOpts = new NetServerOptions().setSsl(true).setKeyStoreOptions(
  new JksOptions()
    .setPath("/path/to/your/server-keystore.jks")
    .setPassword("password-of-your-keystore")
);
NetServer tcpServer = vertx.createNetServer(srvOpts);

Using Buffer (JKS):

Buffer keystoreBuf = vertx.fileSystem().readFileBlocking("/path/to/your/server-keystore.jks");
JksOptions jksOpts = new JksOptions()
  .setValue(keystoreBuf)
  .setPassword("password-of-your-keystore");
NetServerOptions srvOpts = new NetServerOptions().setSsl(true).setKeyStoreOptions(jksOpts);
NetServer tcpServer = vertx.createNetServer(srvOpts);

Using PKCS#12:

NetServerOptions srvOpts = new NetServerOptions().setSsl(true).setPfxKeyCertOptions(
  new PfxOptions()
    .setPath("/path/to/your/server-keystore.pfx")
    .setPassword("password-of-your-keystore")
);
NetServer tcpServer = vertx.createNetServer(srvOpts);

Using PEM:

NetServerOptions srvOpts = new NetServerOptions().setSsl(true).setPemKeyCertOptions(
  new PemKeyCertOptions()
    .setKeyPath("/path/to/your/server-key.pem")
    .setCertPath("/path/to/your/server-cert.pem")
);
NetServer tcpServer = vertx.createNetServer(srvOpts);

Warning: PEM private keys should not be encrypted when using this configuration method.

Server-Side Trust (Client Authentication)

Require client authentication and configure trusted certificates.

Using JKS Truststore:

NetServerOptions srvOpts = new NetServerOptions()
  .setSsl(true)
  .setClientAuth(ClientAuth.REQUIRED)
  .setTrustStoreOptions(
    new JksOptions()
      .setPath("/path/to/your/truststore.jks")
      .setPassword("password-of-your-truststore")
  );
NetServer tcpServer = vertx.createNetServer(srvOpts);

Using PEM:

NetServerOptions srvOpts = new NetServerOptions()
  .setSsl(true)
  .setClientAuth(ClientAuth.REQUIRED)
  .setPemTrustOptions(
    new PemTrustOptions().addCertPath("/path/to/your/server-ca.pem")
  );
NetServer tcpServer = vertx.createNetServer(srvOpts);

Client-Side SSL

Trust All (Dev/Test only):

NetClientOptions cliOpts = new NetClientOptions().setSsl(true).setTrustAll(true);
NetClient tcpClient = vertx.createNetClient(cliOpts);

Hostname Verification:

NetClientOptions cliOpts = new NetClientOptions().setSsl(true).setHostnameVerificationAlgorithm("HTTPS");
NetClient tcpClient = vertx.createNetClient(cliOpts);

Client Trust Configuraton (JKS):

NetClientOptions cliOpts = new NetClientOptions().setSsl(true).setTrustStoreOptions(
  new JksOptions()
    .setPath("/path/to/your/truststore.jks")
    .setPassword("password-of-your-truststore")
);
NetClient tcpClient = vertx.createNetClient(cliOpts);

Client Key/Cert (PEM):

NetClientOptions cliOpts = new NetClientOptions().setSsl(true).setPemKeyCertOptions(
  new PemKeyCertOptions()
    .setKeyPath("/path/to/your/client-key.pem")
    .setCertPath("/path/to/your/client-cert.pem")
);
NetClient tcpClient = vertx.createNetClient(cliOpts);

Self-Signed Certificates for Testing

SelfSignedCertificate cert = SelfSignedCertificate.create();

NetServerOptions srvOpts = new NetServerOptions()
  .setSsl(true)
  .setKeyCertOptions(cert.keyCertOptions())
  .setTrustOptions(cert.trustOptions());

NetServer tcpServer = vertx.createNetServer(srvOpts)
  .connectHandler(socket -> socket.write("Hello!").end())
  .listen(1234, "localhost");

NetClientOptions cliOpts = new NetClientOptions()
  .setSsl(true)
  .setKeyCertOptions(cert.keyCertOptions())
  .setTrustOptions(cert.trustOptions());

NetClient tcpClient = vertx.createNetClient(cliOpts);
tcpClient.connect(1234, "localhost", ar -> {
  if (ar.succeeded()) {
    ar.result().handler(buffer -> System.out.println(buffer));
  } else {
    System.err.println("Woops: " + ar.cause().getMessage());
  }
});

Warning: Do not use self-signed certificates in production.

Advanced TLS Settings

CRL (Certificate Revocation List):

NetClientOptions cliOpts = new NetClientOptions()
  .setSsl(true)
  .setTrustStoreOptions(trustOpts)
  .addCrlPath("/path/to/your/crl.pem");
NetClient tcpClient = vertx.createNetClient(cliOpts);

Cipher Suites:

NetServerOptions srvOpts = new NetServerOptions()
  .setSsl(true)
  .setKeyStoreOptions(keyStoreOpts)
  .addEnabledCipherSuite("ECDHE-RSA-AES128-GCM-SHA256")
  .addEnabledCipherSuite("ECDHE-ECDSA-AES128-GCM-SHA256");
NetServer tcpServer = vertx.createNetServer(srvOpts);

Protocol Versions:

NetServerOptions srvOpts = new NetServerOptions()
  .setSsl(true)
  .setKeyStoreOptions(keyStoreOpts)
  .removeEnabledSecureTransportProtocol("TLSv1")
  .addEnabledSecureTransportProtocol("TLSv1.3");
NetServer tcpServer = vertx.createNetServer(srvOpts);

SSL Engines (OpenSSL vs JDK):

// JDK Engine
NetServerOptions jdkOpts = new NetServerOptions()
  .setSsl(true)
  .setKeyStoreOptions(keyStoreOpts)
  .setJdkSslEngineOptions(new JdkSSLEngineOptions());

// OpenSSL Engine
NetServerOptions opensslOpts = new NetServerOptions()
  .setSsl(true)
  .setKeyStoreOptions(keyStoreOpts)
  .setOpenSslEngineOptions(new OpenSSLEngineOptions());

Server Name Indication (SNI)

Enable SNI on the server to select certificates based on the hostname:

JksOptions keyCertOpts = new JksOptions().setPath("keystore.jks").setPassword("wibble");

NetServer tcpServer = vertx.createNetServer(new NetServerOptions()
    .setKeyStoreOptions(keyCertOpts)
    .setSsl(true)
    .setSni(true)
);

Clients can specify a server name during connection:

NetClient tcpClient = vertx.createNetClient(new NetClientOptions()
    .setTrustStoreOptions(trustOpts)
    .setSsl(true)
);

client.connect(1234, "localhost", "server.name", res -> {
  if (res.succeeded()) {
    System.out.println("Connected!");
  } else {
    System.out.println("Failed: " + res.cause().getMessage());
  }
});

Application-Layer Protocol Negotiation (ALPN)

Used for HTTP/2 negotiation. Java 9+ supports ALPN natively. For Java 8, use OpenSSL or Jetty-ALPN.

Configure via engine options:

// OpenSSL ALPN
NetServerOptions opts = new NetServerOptions()
  .setSsl(true)
  .setUseAlpn(true)
  .setKeyStoreOptions(keyStoreOpts)
  .setOpenSslEngineOptions(new OpenSSLEngineOptions());

Proxy Support

Configure the client to use a SOCKS or HTTP proxy:

NetClientOptions cliOpts = new NetClientOptions()
  .setProxyOptions(new ProxyOptions()
    .setType(ProxyType.SOCKS5)
    .setHost("localhost")
    .setPort(1080)
    .setUsername("username")
    .setPassword("secret")
  );
NetClient tcpClient = vertx.createNetClient(cliOpts);

HTTP Server Implementation

Basic HTTP Server

HttpServer httpServer = vertx.createHttpServer();

Configuration

HttpServerOptions httpOpts = new HttpServerOptions().setMaxWebsocketFrameSize(1000000);
HttpServer httpServer = vertx.createHttpServer(httpOpts);

HTTP/2 Support

h2 (TLS):

HttpServerOptions httpOpts = new HttpServerOptions()
    .setUseAlpn(true)
    .setSsl(true)
    .setKeyStoreOptions(new JksOptions().setPath("/path/to/my/keystore"));
HttpServer httpServer = vertx.createHttpServer(httpOpts);

h2c (Clear text):

Ensure SSL is disabled. The server handles upgrades from HTTP/1.1 or direct PRI requests.

Note: Most browsers do not support h2c; use h2 for web-facing servers.

Starting HTTP Server

HttpServer httpServer = vertx.createHttpServer();
httpServer.listen(8080, "myhost.com", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed to bind!");
  }
});

Request Handling

Set a requestHandler to process incoming HttpServerRequest objects:

HttpServer httpServer = vertx.createHttpServer();
httpServer.requestHandler(request -> {
  // Access uri, method, headers, params, etc.
  HttpServerResponse response = request.response();
  response.end("Hello world");
});
httpServer.listen(8080);

Request Details

  • version(): HTTP version.
  • method(): HTTP method (GET, POST, etc.).
  • uri(): Relative URI.
  • path(): Path component of the URI.
  • query(): Query string.
  • headers(): Returns a MultiMap (case-insensitive).
  • host(): Host header (HTTP/1.x) or :authority (HTTP/2).
  • params(): Request parameters from the query string.
  • remoteAddress(): Client address.
  • absoluteURI(): Full absolute URI.

Reading the Request Body

Since bodies can be large, data arrives in chunks. Use a handler or bodyHandler for the complete body:

request.bodyHandler(totalBuffer -> {
  System.out.println("Full body received, length = " + totalBuffer.length());
});

Handling Forms and Uploads

For multipart forms, enable expectation first:

server.requestHandler(request -> {
  request.setExpectMultipart(true);
  request.endHandler(v -> {
    MultiMap formAttributes = request.formAttributes();
  });
});

Handle file uploads:

server.requestHandler(request -> {
  request.setExpectMultipart(true);
  request.uploadHandler(upload -> {
    System.out.println("Got a file upload " + upload.name());
    upload.streamToFileSystem("uploads/" + upload.filename());
  });
});

Warning: Validate filenames from uploads to prevent path traversal attacks.

HTTP/2 Custom Frames

request.customFrameHandler(frame -> {
  System.out.println("Received a frame type=" + frame.type() +
      " payload" + frame.payload().toString());
});

Network Activity Logging

HttpServerOptions httpOpts = new HttpServerOptions().setLogActivity(true);
HttpServer httpServer = vertx.createHttpServer(httpOpts);

Tags: Vert.x tcp HTTP SSL/TLS Non-blocking

Posted on Fri, 08 May 2026 03:09:39 +0000 by jraede