Java 和 Nagle 插图

标签 java tcp client-server

我试图在一个简单的客户端-服务器程序中说明 Nagle 算法。但我不太明白,也无法将其清楚地打印给我。

在我的示例中,客户端只是生成从 1 到 1024 的整数并将它们发送到服务器。服务器只是将这些 int 转换为十六进制字符串并将它们发送回客户端。

我所做的几乎所有更改都以相同的结果结束。 int 以 256 个 int 的 block 的形式发送和重新发送.. 我在两侧尝试了 setTcpNoDelay(true) 以查看更改,但这在我的控制台中给出了相同的结果。 (但不是在 wireshark 中,我发现服务器和客户端之间发送的数据包数量有很大差异) 但我的目标是能够在控制台中看到它,我猜有一些 ObjectOutputStream 缓冲区或类似的东西可以支撑一切?

当我将 output = new PrintWriter(client.getOutputStream(), true) 更改为 false (truefalse:autoFlush - boolean 值;如果 true,则 printlnprintfformat 方法将刷新输出缓冲区)我的服务器不再将任何输出返回给客户端。

基本上,我的目标是在启动时将服务器和/或客户端的 true 或 false 作为参数来设置 TcpNoDelay,以便清楚地看到控制台中输入/输出的差异。我不确定所使用的所有内容,因此欢迎任何帮助来解决这个问题。

服务器:

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");
        }
    }
}

客户端:

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();
        }
    }
}

最佳答案

您将无法看到差异,因为 readLine() 将等待直到读取 eol。要查看差异,请使用二进制数据。使传出流写入由 10ms sleep 分隔的 64 字节 block 。使传入流读取 1024 字节的 block 。当 tcpNoDelay 为真时,传入流将在每次读取操作时读取大约 64 个字节。当 tcpNoDelay 为 false 时,传入流将读取更多字节。您可以记录每次读取操作读取的平均字节数,因此差异很明显。并且始终使用两台机器进行测试,因为操作系统可以优化环回流。

关于Java 和 Nagle 插图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2536768/

相关文章:

node.js - 使用分离的客户端和服务器进行身份验证

java - 改变家庭背景

Java 框然后在 == 运算符上加宽

java - 设置使用 HttpServletRequest.getSession(true) 创建的 JSESSIONID cookie 的路径

c - FTP 客户端无法与我实现的 TCP 服务器通信

Java 套接字和黑莓编程

java - 设置 Java 多线程亲和性

shell - 使用 netcat 将命令发送到套接字

c++ - 套接字编程学习

java - 什么样的策略更有效率 : create a new socket or use one already created?