Disable Nagle’s Algorithm with OkHttp or Scarlet

Roger Iyengar
3 min readNov 4, 2020

TCP includes a feature called Nagle’s Algorithm that will postpone sending small messages, in hopes of combining them with future messages. This can save bandwidth, because TCP has to include additional information along with each segment. Combining multiple small messages into a single segment reduces the overall bandwidth used. However, this increases the time it will take for some small messages to be received.

If you want all of your messages to be sent with the lowest latency possible, and you are willing to use additional bandwidth, you should disable Nagle’s Algorithm. WebSocket connections made with OkHttp have Nagle’s Algorithm enabled by default. There was an issue about this back in 2013, and the fix was to add the socketFactory and sslSocketFactory options.

To disable Nagle’s Algorithm using these options, you have to create classes that extend SocketFactory and SSLSocketFactory and call setTcpNoDelay(true) on their underlying sockets. You can use the following classes I created, which use composition:

SocketFactory

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import javax.net.SocketFactory;

public class SocketFactoryTcpNoDelay extends SocketFactory {
private final SocketFactory socketFactory;

public SocketFactoryTcpNoDelay() {
socketFactory = SocketFactory.getDefault();
}

@Override
public Socket createSocket() throws IOException {
Socket socket = socketFactory.createSocket();
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port) throws IOException {
Socket socket = socketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws
IOException {
Socket socket = socketFactory.createSocket(host, port, localHost, localPort);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = socketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = socketFactory.createSocket(address, port, localAddress, localPort);
socket.setTcpNoDelay(true);
return socket;
}
}

SSLSocketFactory

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class SSLSocketFactoryTcpNoDelay extends SSLSocketFactory {
private final X509TrustManager trustManager;
private final SSLSocketFactory sslSocketFactory;

public SSLSocketFactoryTcpNoDelay() throws NoSuchAlgorithmException, KeyStoreException,
KeyManagementException {
// From
// https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/-builder/ssl-socket-factory
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
}

public X509TrustManager getTrustManager() {
return trustManager;
}

public SSLSocketFactory getSslSocketFactory() {
return sslSocketFactory;
}

@Override
public String[] getDefaultCipherSuites() {
return sslSocketFactory.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return sslSocketFactory.getSupportedCipherSuites();
}

@Override
public Socket createSocket() throws IOException {
Socket socket = sslSocketFactory.createSocket();
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws
IOException {
Socket socket = sslSocketFactory.createSocket(s, host, port, autoClose);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws
IOException {
Socket socket = sslSocketFactory.createSocket(host, port, localHost, localPort);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(host, port);
socket.setTcpNoDelay(true);
return socket;
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port, localAddress, localPort);
socket.setTcpNoDelay(true);
return socket;
}
}

Putting It Together

You can create an OkHttpClient that uses these classes as follows:

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
try {
SSLSocketFactoryTcpNoDelay sSLSocketFactoryTcpNoDelay =
new SSLSocketFactoryTcpNoDelay();
okHttpClientBuilder.sslSocketFactory(sSLSocketFactoryTcpNoDelay.getSslSocketFactory(),
sSLSocketFactoryTcpNoDelay.getTrustManager());
} catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
// Handle Exception
}
SocketFactoryTcpNoDelay socketFactoryTcpNoDelay = new SocketFactoryTcpNoDelay();
okHttpClientBuilder.socketFactory(socketFactoryTcpNoDelay);
OkHttpClient okHttpClient = okHttpClientBuilder.build();

WebSocket connections that your OkHttpClient makes will have Nagle’s Algorithm disabled.

Scarlet

Scarlet is a library from Tinder that uses OkHttp for WebSocket connections. If you pass the OkHttpClient that you created above to OkHttpClientUtils#newWebSocketFactory, all WebSocket connections made with Scarlet will have Nagle’s Algorithm disabled.

Note that all Scarlet WebSocket connections use OkHttp. As of writing this post, Scarlet is still using OkHttp version 3.11. OkHttp 4 has been out for over a year. If you are starting a new project, consider using OkHttp directly, rather than using Scarlet.

--

--