Java and Nagle illustration
Asked Answered
W

1

6

I'm trying to illustrate the Nagle algorithm in a simple client-server program. But I can't quite figure it out, or get it to be printed to me clearly.

In my example, the client just generates int's from 1 to 1024 and sends these to the server. The server just converts these int's to a hex string and sends them back to the client.

Pretty much everything i change ends up with the same results. The int's are sent and resent in blocks of 256 int's.. I tried setTcpNoDelay(true) on both sides to see a change but this gives the same result in my console. (but not in wireshark, i see a big difference in amount of packets sent between server and client) But my goal is to be able to see it in the console, I'm guessing there is some buffer of ObjectOutputStream or similar holding everything up?

When I change output = new PrintWriter(client.getOutputStream(), true) to false (The true or false: autoFlush - A boolean; if true, the println, printf, or format methods will flush the output buffer) my server does not give any output back to the client anymore.

Basically my goal is to just give true or false with server and/or client as an argument to set the TcpNoDelay, when started, to clearly see the difference in input/output in the console. I'm not sure about everything used so any help to clear this out is welcome.

The server:

package Networks.Nagle;

import java.io.*;
import java.net.*;
import java.util.*;

public class NagleDemoServer
{
    private static ServerSocket serverSocket;
    private static final int PORT = 1234;

    public static void main(String[] args) throws IOException
    {
        int received = 0;
        String returned; 
        ObjectInputStream input = null;
        PrintWriter output = null;
        Socket client;

        try
        {
            serverSocket = new ServerSocket(PORT);
            System.out.println("\nServer started...");
        }
        catch (IOException ioEx)
        {
            System.out.println("\nUnable to set up port!");
            System.exit(1);
        }

        while(true)
        {
            client = serverSocket.accept();
            client.setTcpNoDelay(true);

            System.out.println("\nNew client accepted.\n");

            try
            {
                input = new ObjectInputStream(client.getInputStream());
                output = new PrintWriter(client.getOutputStream(), true);

                while( true )
                {
                    received = input.readInt();
                    returned = Integer.toHexString(received);
                    System.out.print(" " + received);
                    output.println(returned.toUpperCase());

                }
            }
            catch(EOFException eofEx)
            {
                output.flush();
                System.out.println("\nEnd of client data.\n");
            }
            catch(SocketException sEx)
            {
                System.out.println("\nAbnormal end of client data.\n");
            }
            catch(IOException ioEx)
            {
                ioEx.printStackTrace();
            }

            input.close();
            output.close();
            client.close();
            System.out.println("\nClient closed.\n");
        }
    }
}

The client:

package Networks.Nagle;

import java.io.*;
import java.net.*;
import java.util.*;

public class NagleDemoClient
{
    private static InetAddress host;
    private static final int PORT = 1234;

    public static void main(String[] args)
    {
        Socket socket = null;

        try
        {
            host = InetAddress.getByName("localhost");

            socket = new Socket(host, PORT);

            socket.setTcpNoDelay(true);
            socket.setSendBufferSize(64);

            System.out.println("Send Buffer: " + socket.getSendBufferSize());
            System.out.println("Timeout: " + socket.getSoTimeout());
            System.out.println("Nagle deactivated: " + socket.getTcpNoDelay());

        }
        catch(UnknownHostException uhEx)
        {
            System.out.println("\nHost ID not found!\n");
            System.exit(1);
        }
        catch(SocketException sEx)
        {
            sEx.printStackTrace();
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }

        NagleClientThread client = new NagleClientThread(socket);
        NagleReceiverThread receiver = new NagleReceiverThread(socket);

        client.start();
        receiver.start();

        try
        {
            client.join();
            receiver.join();

            socket.close();
        }
        catch(InterruptedException iEx)
        {
            iEx.printStackTrace();
        } 
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }

        System.out.println("\nClient finished.");
    }
}


class NagleClientThread extends Thread
{
    private Socket socket;

    public NagleClientThread(Socket s)
    {
        socket = s;

    }

    public void run() 
    {
        try
        {
            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

            for( int i = 1; i < 1025; i++)
            {
                output.writeInt(i);
                sleep(10);
            } 

            output.flush();
            sleep(1000);
            output.close();
        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
        catch(InterruptedException iEx)
        {
            iEx.printStackTrace();
        }
    }
}


class NagleReceiverThread extends Thread
{
    private Socket socket;

    public NagleReceiverThread(Socket s)
    {
        socket = s;
    }

    public void run()
    {
        String response = null;
        BufferedReader input = null;

        try
        {
            input = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));

            try
            {
                while( true ) 
                {
                    response = input.readLine(); 
                    System.out.print(response + " ");
                } 
            }
            catch(Exception e)
            {
                System.out.println("\nEnd of server data.\n");
            }    

            input.close();

        }
        catch(IOException ioEx)
        {
            ioEx.printStackTrace();
        }
    }
}
Weasand answered 29/3, 2010 at 9:4 Comment(0)
F
0

You won't be able to see the difference because readLine() will wait until an eol is read. To see the difference, use binary data. Make the outgoing stream write blocks of 64 bytes separated by 10ms sleeps. Make the incoming stream read blocks of 1024 bytes. When tcpNoDelay is true, the incoming stream will be read about 64 bytes at each read operation. When tcpNoDelay is false, the incoming stream will read much more bytes. You can log the average number of bytes read in each read operation, so the difference is obvious. And also always test using two machines, because the OS can optimize loopback streams.

Fetial answered 22/4, 2011 at 22:18 Comment(1)
You can test with one peer just sending and another just receiving and logging. But based on your example, I understand you want a server trip for benchmarking purposes. In that case, let your server with tcpNoDelay always on, and make it read a block of 4096 bytes and promptly answer with whatever number of bytes it just have read. You will see difference by just varying tcpNoDelay on the client.Fetial

© 2022 - 2024 — McMap. All rights reserved.