Forcing a TCP socket to flush in Java

10,186

Try this create a simple server :

public static void main(String[] args) throws IOException {
    ServerSocket svr = new ServerSocket(1234);
    Socket s = svr.accept();
    byte b4[] = new byte[4];
    new DataInputStream(s.getInputStream()).readFully(b4);
    s.getOutputStream().write(b4);
}

Run your client code, but measure the time:

public static void main(String[] args) throws IOException {
    Socket sock = new Socket("localhost", 1234);
    OutputStream output = sock.getOutputStream();
    long t1 = System.currentTimeMillis();
    output.write(new byte[]{(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef});
    byte b4[] = new byte[4];
    new DataInputStream(sock.getInputStream()).readFully(b4);
    long t2 = System.currentTimeMillis();
    System.out.println("t2-t1="+(t2-t1));
}

When I did this I got an output of 15 milliseconds. This is round trip including both read and write. If you run this and get something dramatically different, then you probably need to fix something in your network configuration. If this gives you about 15 milliseconds you need to look at the difference between this code and your code.The setTcpNoDelay might have an effect but for for me it wasn't noticeable.

Share:
10,186
Ethan White
Author by

Ethan White

Amateur software developer and avid musician they/them

Updated on July 26, 2022

Comments

  • Ethan White
    Ethan White almost 2 years

    I'm developing a protocol that requires a handshake that consists of several pieces of information going back and forth in each direction, with several pieces dependent on the previous pieces (think like SSL/TLS handshake). I'm trying to implement it in Java.

    A lot of my packets are only 20-30 bytes, whereas my computer seems to think it needs to buffer hundreds of bytes before it sends anything; even if I wait 5-10 seconds (with Thread.sleep), it still won't actually send it until I close the socket.

    How can I get Java/my computer to send what's in its buffer?

    On other forums (and SO), a lot of people have been using arguments like "that's not how TCP works", "you shouldn't actually need that." If I need to send 10 packets each way, each one depends on the last, each one waits for 300ms in the TCP stack, I'm looking at 6s for my handshake, which is completely unusable. (Mind you, I can't get it to send at all, no matter how much I call flush on the socket; I can't get it sent at all until I close the socket).

    I have considered just sending a packet plus enough useless padding to force the TCP implementation to force it, that seems like a bad solution.

    (Simplified, extracted) Code:

    Socket sock = new Socket("localhost", 1234);
    OutputStream output = sock.getOutputStream();
    output.write(new byte[] {(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef});
    output.flush();
    Thread.sleep(5000);
    

    [I've tried with PrintWriter, OutputStreamWriter; they don't help.]

  • Ethan White
    Ethan White over 8 years
    When I run your code, it works; I was using netcat to recieve. Strange.
  • user207421
    user207421 over 8 years
    @EthanWhite Don't use this code in production. It suffers from the classic TCP problem of ignoring the count returned by the read() method. In this case you should probably use DataInputStream.readFully() to fill the buffers.
  • nos
    nos over 8 years
    @EthanWhite If you're sending non-printable characters to netcat, you're not going to see anything on your screen. Depending on which netcat version you're using, netcat might also buffer the data before it writes anything it received to the screen or to a file.