`
san_yun
  • 浏览: 2638606 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

java socket参数详解:TcpNoDelay

 
阅读更多
TcpNoDelay=false,为启用nagle算法,也是默认值。 Nagle算法的立意是良好的,避免网络中充塞小封包,提高网络的利用率。但是当Nagle算法遇到delayed ACK悲剧就发生了。Delayed ACK的本意也是为了提高TCP性能,跟应答数据捎带上ACK,同时避免糊涂窗口综合症,也可以一个ack确认多个段来节省开销。悲剧发生在这种情况,假设一端发送数据并等待另一端应答,协议上分为头部和数据,发送的时候不幸地选择了write-write,然后再read,也就是先发送头部,再发送数据,最后等待应答。
实验模型:
发送端(客户端)
write(head);
write(body);
read(response);
接收端(服务端)
read(request); 
process(request); 
write(response);
这里假设head和body都比较小,当默认启用nagle算法,并且是第一次发送的时候,根据nagle算法,第一个段head可以立即发送,因为没有等待确认的段;接收端(服务端)收到head,但是包不完整,继续等待body达到并延迟ACK;发送端(客户端)继续写入body,这时候nagle算法起作用了,因为head还没有被ACK,所以body要延迟发送。这就造成了发送端(客户端)和接收端(服务端)都在等待对方发送数据的现象:
发送端(客户端)等待接收端ACK head以便继续发送body;
接收端(服务端)在等待发送方发送body并延迟ACK,悲剧的无以言语。
这种时候只有等待一端超时并发送数据才能继续往下走。
代码:
发送端代码
[java] view plaincopyprint?

    package socket.nagle; 
     
    import java.io.*; 
    import java.net.*; 
    import org.apache.log4j.Logger; 
     
    public class Client { 
        private static Logger logger = Logger.getLogger(Client.class); 
        public static void main(String[] args) throws Exception { 
            // 是否分开写head和body 
            boolean writeSplit = true; 
            String host = "localhost"; 
            logger.debug("WriteSplit:" + writeSplit); 
     
            Socket socket = new Socket(); 
            socket.setTcpNoDelay(false); 
            socket.connect(new InetSocketAddress(host, 10000)); 
     
            InputStream in = socket.getInputStream(); 
            OutputStream out = socket.getOutputStream(); 
            BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
     
            String head = "hello "; 
            String body = "world\r\n"; 
            for (int i = 0; i < 10; i++) { 
                long label = System.currentTimeMillis(); 
                if (writeSplit) { 
                    out.write(head.getBytes()); 
                    out.write(body.getBytes()); 
                } else { 
                    out.write((head + body).getBytes()); 
                } 
                String line = reader.readLine(); 
                logger.debug("RTT:" + (System.currentTimeMillis() - label) + ", receive: " + line); 
            } 
            in.close(); 
            out.close(); 
            socket.close(); 
        } 
    } 

接收端代码
[java] view plaincopyprint?

    package socket.nagle; 
     
    import java.io.*; 
    import java.net.*; 
     
    import org.apache.log4j.Logger; 
     
    public class Server { 
        private static Logger logger = Logger.getLogger(Server.class); 
     
        public static void main(String[] args) throws Exception { 
            ServerSocket serverSocket = new ServerSocket(); 
            serverSocket.bind(new InetSocketAddress(10000)); 
            logger.debug(serverSocket); 
            logger.debug("Server startup at 10000"); 
            while (true) { 
                Socket socket = serverSocket.accept(); 
                InputStream in = socket.getInputStream(); 
                OutputStream out = socket.getOutputStream(); 
     
                while (true) { 
                    try { 
                        BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
                        String line = reader.readLine(); 
                        logger.debug(line); 
                        out.write((line + "\r\n").getBytes()); 
                    } catch (Exception e) { 
                        break; 
                    } 
                } 
            } 
        } 
    } 

实验结果:
[plain] view plaincopyprint?

    [test5@cent4 ~]$ java socket.nagle.Server 
    1    [main] DEBUG socket.nagle.Server - ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=10000] 
    6    [main] DEBUG socket.nagle.Server - Server startup at 10000 
    4012 [main] DEBUG socket.nagle.Server - hello world 
    4062 [main] DEBUG socket.nagle.Server - hello world 
    4105 [main] DEBUG socket.nagle.Server - hello world 
    4146 [main] DEBUG socket.nagle.Server - hello world 
    4187 [main] DEBUG socket.nagle.Server - hello world 
    4228 [main] DEBUG socket.nagle.Server - hello world 
    4269 [main] DEBUG socket.nagle.Server - hello world 
    4310 [main] DEBUG socket.nagle.Server - hello world 
    4350 [main] DEBUG socket.nagle.Server - hello world 
    4390 [main] DEBUG socket.nagle.Server - hello world 
    4392 [main] DEBUG socket.nagle.Server - 
    4392 [main] DEBUG socket.nagle.Server -  

实验1:当WriteSplit=true and TcpNoDelay=false 启用nagle算法
[plain] view plaincopyprint?

    [test5@cent4 ~]$ java socket.nagle.Client 
    0    [main] DEBUG socket.nagle.Client - WriteSplit:true 
    52   [main] DEBUG socket.nagle.Client - RTT:12, receive: hello world 
    95   [main] DEBUG socket.nagle.Client - RTT:42, receive: hello world 
    137  [main] DEBUG socket.nagle.Client - RTT:41, receive: hello world 
    178  [main] DEBUG socket.nagle.Client - RTT:41, receive: hello world 
    218  [main] DEBUG socket.nagle.Client - RTT:40, receive: hello world 
    259  [main] DEBUG socket.nagle.Client - RTT:40, receive: hello world 
    300  [main] DEBUG socket.nagle.Client - RTT:41, receive: hello world 
    341  [main] DEBUG socket.nagle.Client - RTT:41, receive: hello world 
    382  [main] DEBUG socket.nagle.Client - RTT:41, receive: hello world 
    422  [main] DEBUG socket.nagle.Client - RTT:40, receive: hello world 

可以看到,每次请求到应答的时间间隔都在40ms,除了第一次。linux的delayed ack是40ms,而不是原来以为的200ms。第一次立即ACK,似乎跟linux的quickack mode有关,这里我不是特别清楚,
其实问题不是出在nagle算法身上的,问题是出在write-write-read这种应用编程上。禁用nagle算法可以暂时解决问题,但是禁用 nagle算法也带来很大坏处,网络中充塞着小封包,网络的利用率上不去,在极端情况下,大量小封包导致网络拥塞甚至崩溃。在这种情况下,其实你只要避免write-write-read形式的调用就可以避免延迟现象,如下面这种情况发送的数据不要再分割成两部分。
实验2:当WriteSplit=false and TcpNoDelay=false 启用nagle算法
[plain] view plaincopyprint?

    [test5@cent4 ~]$ java socket.nagle.Client 
    0    [main] DEBUG socket.nagle.Client - WriteSplit:false 
    27   [main] DEBUG socket.nagle.Client - RTT:4, receive: hello world 
    31   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    34   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    38   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    42   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    44   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    47   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    50   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    53   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    54   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 

实验3:当WriteSplit=true and TcpNoDelay=true 禁用nagle算法
[plain] view plaincopyprint?

    [test5@cent4 ~]$ java socket.nagle.Client 
    0    [main] DEBUG socket.nagle.Client - WriteSplit:true 
    25   [main] DEBUG socket.nagle.Client - RTT:6, receive: hello world 
    28   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    31   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    33   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    35   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    41   [main] DEBUG socket.nagle.Client - RTT:6, receive: hello world 
    49   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    52   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    56   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    59   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 

实验4:当WriteSplit=false and TcpNoDelay=true 禁用nagle算法
[plain] view plaincopyprint?

    [test5@cent4 ~]$ java socket.nagle.Client 
    0    [main] DEBUG socket.nagle.Client - WriteSplit:false 
    21   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    23   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    27   [main] DEBUG socket.nagle.Client - RTT:3, receive: hello world 
    30   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    32   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    35   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    38   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    41   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 
    43   [main] DEBUG socket.nagle.Client - RTT:1, receive: hello world 
    46   [main] DEBUG socket.nagle.Client - RTT:2, receive: hello world 

实验2到4,都没有出现延时的情况。
注意:以上实验在windows上测试下面的代码,客户端和服务器必须分在两台机器上,似乎winsock对loopback连接的处理不一样。下面的我的做法是:服务端与客户端都在一台Linux机上。
分享到:
评论

相关推荐

    TcpNoDelay

    在Java中,可以通过`Socket`对象的`setTcpNoDelay`方法来设置这个选项。例如,`socket.setTcpNoDelay(true)`会关闭Nagle算法,而`socket.setTcpNoDelay(false)`则会启用它。在上述代码示例中,`socket.setTcpNoDelay...

    java实现流量控制流量控制

    在Java中,我们可以利用Socket类和相关的API来实现TCP连接,并通过设置缓冲区大小来间接控制流量。 1. **Socket类和BufferedInputStream/BufferedOutputStream** Java的Socket类提供了与服务器建立TCP连接的功能。...

    mina框架--MINA框架是对java的NIO包的一个封装

    5. `acceptor` 的配置还包括了设置重用地址、接收和发送缓冲区大小、TCP的非延迟模式(TcpNoDelay)以及监听队列的长度(backlog)。这些配置对服务器的性能和行为有着直接影响。 6. 最后,通过设置处理器(Handler...

    用amoeba配置mysql代理.pdf

    - `netBufferSize`定义了套接字发送和接收缓冲区大小,单位为KB,`tcpNoDelay`参数用于开启或关闭TCP_NODELAY,即禁用Nagle算法。 4. **数据库服务器列表配置** 在`dbserverlist`节点下,可以配置多个数据库...

    用amoeba配置mysql代理[归类].pdf

    另外,`netBufferSize`参数用于设置socket发送和接收的缓冲区大小。 5. TCP_NODELAY配置:这是一个与TCP网络协议相关的参数,用于控制是否启用Nagle算法。Nagle算法是一种减少小TCP包发送数量的算法,可以减少网络...

    Hadoop的全部配置

    - **`ipc.server.tcpnodelay--false`**:禁用了TCP无延迟选项,这可能会影响网络性能。 - **`mapreduce.jobhistory.client.thread-count--10`**:设置了MapReduce作业历史服务客户端线程池大小为10个线程。 - **`...

    hadoop源码阅读总结

    - `Handlers`从队列中取出`Call`对象,解析参数为`Invocation`对象,利用Java反射API调用服务端的实例对象处理请求,获取返回结果,并将结果封装到`Call`的`response`属性中。 - 处理完成后,调用`Responder`的`...

    linux下openmeetings安装配置.pdf

    xvfb-run -a soffice -headless -nologo -norestore -nofirststartwizard -accept="socket,host=127.0.0.1,port=8100,tcpNoDelay=1;urp;" ``` **第五步:安装其他依赖** 1. 安装ImageMagick、Ghostscript、...

    win2008,win8,win10网络加速TCP参数

    标题中的“win2008,win8,win10网络加速TCP参数”指的是针对Windows Server 2008、Windows 8以及Windows 10操作系统优化网络性能的一系列TCP/IP参数调整。这些参数通常涉及到TCP窗口大小、快速重传、延迟发送、拥塞...

    AIX上TSM性能调整

    - `TCPNODELAY`: 禁用Nagle算法,设置为Yes。 以上各项措施的综合运用,可以显著提高AIX平台上TSM服务器的性能表现,从而构建一个更加高效可靠的存储备份环境。在实际应用过程中,还需要根据具体情况不断调整和...

    Win10玩游戏优化设置教程.docx

    对于包含`DhcpIPAddress`项的每个文件夹,新建两个DWORD值,分别命名为`TcpAckFrequency`和`TCPNoDelay`。 4. 双击这两个新创建的值,并将它们的键值均设为`1`,然后点击【确定】保存设置。 - **功能解析**: -...

    ROS脚本\ROS流控精华最终版.rar

    例如,使用TCPNoDelay选项可以减少网络延迟,而精心设计的节点和话题发布频率可以确保系统在实时性要求高的场景下运行良好。 6. **多机通信**:ROS也支持多机器人系统,节点可以跨越多个计算设备,通过ROS Master...

    ros流控脚本

    9. **TCPNoDelay**: 对于使用TCP的ROS通信,`TCPNoDelay`标志会影响是否启用Nagle算法。关闭该标志可以减少延迟但增加网络负载,反之则可优化带宽使用。 10. **Rosbridge and WebSocket**: 如果ROS需要与Web应用或...

    个人配置VTL核心命令

    - `tcpnodelay`: 是否启用Nagle算法,这里设置为`yes`表示禁用。 - `DEVCONFIG`: 设备配置文件路径。 - `VOLUMEHISTORY`: 卷历史记录文件路径。 - `ACTIVELOGDirectory`: 活动日志目录路径。 - `ARCHLOGDirectory`: ...

    IBM小型机培训

    - `tcpnodelay`:是否控制TCP数据的延迟发送。 - 修改配置的方式有两种:直接编辑配置文件(位于$SYBASE用户的`server_name.cfg`文件中)或使用`sp_configure`存储过程进行更改。 6. **数据库客户端工具** - **...

    优化Windows 7系统,提高运行速度.docx

    - 修改注册表,创建新的QWORD值`TCPNoDelay`,位于`HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/MSMQ/Parameters`,数值设为1,基数设为16进制。 - 找到`TcpAckFrequency`,同样创建QWORD值,位于`HKEY_LOCAL_MACHINE/...

    电脑管家更改游戏配置教程.pdf

    4. 在含有`DhcpIPAddress`的每个文件夹中,右键新建两个DWORD(32位)值,分别命名为`TcpAckFrequency`和`TCPNoDelay`。 5. 双击这两个新建的键,将它们的数值数据均设置为【1】,然后点击【确定】。 这个操作会...

    电脑管家更改游戏配置教程.docx

    4. 分别将这两个新建的值命名为【TcpAckFrequency】和【TCPNoDelay】。 5. 双击这两个新创建的值,将它们的数值数据都设置为【1】,然后点击【确定】。 6. 请注意,如果在其他【Interfaces】文件夹下也发现有...

    深入浅析Netty 在 Dubbo 中是如何应用的

    在创建过程中,会设置一些基本的选项,例如keepAlive、tcpNoDelay等。 在Netty的Bootstrap对象中,会使用ChannelPipelineFactory来创建一个ChannelPipeline对象,该对象负责处理网络数据的编码和解码。在Dubbo中,...

Global site tag (gtag.js) - Google Analytics