- 浏览: 653848 次
- 性别:
- 来自: 广州
-
文章分类
- 全部博客 (144)
- grails (2)
- jboss (2)
- html5 (2)
- maven (9)
- mysql (9)
- android (13)
- java (39)
- javascript (16)
- flex (3)
- oracle (7)
- ubuntu (5)
- tomcat (1)
- ssh (1)
- iphone (0)
- eclipse (5)
- jquery (4)
- web (7)
- ibatis (7)
- dwr (5)
- hibernate (1)
- ws (0)
- freemarker (4)
- css (2)
- webservice (1)
- html (2)
- jsp (4)
- os (5)
- xp (4)
- cpu (2)
- sql (5)
- db (2)
- 数据库 (5)
- log4j.properties (1)
- date (1)
- map (1)
- log4j (1)
- java spring dwr (1)
- (2)
- xml (4)
- ajax (3)
- servlet (1)
- xmlhttp (1)
- linux (2)
- socket (2)
- java,多线程 (1)
- android,java,os (1)
- Android,java (2)
- spring (1)
- java,dwr (1)
- js (2)
- 回调 (1)
- java,event (1)
- java ,注解, (1)
- avd (1)
- sdk (1)
- maven,java,xml,nexus (1)
- java,maven,nexus (1)
- Maven,Android,i-jetty,java (1)
- web js 参数 (1)
- java mac os (1)
- mac os (2)
- mac (1)
- 环境 (1)
- 搭建 (1)
- adt (1)
- adb (1)
- iOS (1)
最新评论
-
lhs472194266:
可以 X-Forwarded-For 可以轻易的被伪造
Java获取IP地址:request.getRemoteAddr()警惕 -
Nabulio:
图片全部加载不出来
maven 建立本地仓库 -
wahaha603:
...
Properties的相对路径以及文件的读取操作 -
一别梦心:
图片没了,楼主补充一下吧
maven 建立本地仓库 -
a_jie1981:
a_jie1981 写道试试http://www.findma ...
maven 建立本地仓库
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,悲剧的无以言语。这种时候只有等待一端超时并发送数据才能继续往下走。
正因为nagle算法和delayed
ack的影响,再加上这种write-write-read的编程方式造成了很多网贴在讨论为什么自己写的网络程序性能那么差。然后很多人会在帖子里建议
禁用Nagle算法吧,设置TCP_NODELAY为true即可禁用nagle算法。但是这真的是解决问题的唯一办法和最好办法吗?
其实问题不是出在nagle算法身上的,问题是出在write-write-read这种应用编程上。禁用nagle算法可以暂时解决问题,但是禁用
nagle算法也带来很大坏处,网络中充塞着小封包,网络的利用率上不去,在极端情况下,大量小封包导致网络拥塞甚至崩溃。因此,能不禁止还是不禁止的
好,后面我们会说下什么情况下才需要禁用nagle算法。对大多数应用来说,一般都是连续的请求——应答模型,有请求同时有应答,那么请求包的ACK其实
可以延迟到跟响应一起发送,在这种情况下,其实你只要避免write-write-read形式的调用就可以避免延迟现象,利用writev做聚集写或者
将head和body一起写,然后再read,变成write-read-write-read的形式来调用,就无需禁用nagle算法也可以做到不延
迟。
writev是系统调用,在Java里是用到GatheringByteChannel
.write(ByteBuffer[]
srcs, int offset, int length)方法来做聚集写。这里可能还有一点值的提下,很多同学看java
nio框架几乎都不用这个writev调用,这是有原因的。主要是因为Java的write本身对ByteBuffer有做临时缓存,而writev没有
做缓存,导致测试来看write反而比writev更高效,因此通常会更推荐用户将head和body放到同一个Buffer里来避免调用writev。
下面我们将做个实际的代码测试来结束讨论。这个例子很简单,客户端发送一行数据到服务器,服务器简单地将这行数据返回。客户端发送的时候可以选择分两次
发,还是一次发送。分两次发就是write-write-read,一次发就是write-read-write-read,可以看看两种形式下延迟的差
异。注意,在windows上测试下面的代码,客户端和服务器必须分在两台机器上,似乎winsock对loopback连接的处理不一样。
服务器源码:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(8000)); System.out.println("Server startup at 8000"); for (;;) { 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(); out.write((line + "\r\n").getBytes()); } catch (Exception e) { break; } } } } }
服务端绑定到本地8000端口,并监听连接,连上来的时候就阻塞读取一行数据,并将数据返回给客户端。
客户端代码:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { // 是否分开写head和body boolean writeSplit = false; String host = "localhost"; if (args.length >= 1) { host = args[0]; } if (args.length >= 2) { writeSplit = Boolean.valueOf(args[1]); } System.out.println("WriteSplit:" + writeSplit); Socket socket = new Socket(); socket.connect(new InetSocketAddress(host, 8000)); 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(); System.out.println("RTT:" + (System.currentTimeMillis() - label) + " ,receive:" + line); } in.close(); out.close(); socket.close(); } }
客户端通过一个writeSplit变量来控制是否分开写head和body,如果为true,则先写head再写body,否则将head加上body一次写入。客户端的逻辑也很简单,连上服务器,发送一行,等待应答并打印RTT,循环10次最后关闭连接。
首先,我们将writeSplit设置为true,也就是分两次写入一行,在我本机测试的结果,我的机器是xp
WriteSplit:true RTT:8 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world RTT:39 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world RTT:40 ,receive:hello world
可以看到,每次请求到应答的时间间隔都在40ms,除了第一次。linux的delayed ack是40ms,而不是原来以为的200ms。第一次立即ACK,似乎跟linux的quickack mode有关,这里我不是特别清楚,有比较清楚的同学请指教。
接下来,我们还是将writeSplit设置为true,但是客户端禁用nagle算法,也就是客户端代码在connect之前加上一行:
Socket socket = new Socket(); socket.setTcpNoDelay(true); socket.connect(new InetSocketAddress(host, 8000));
再跑下测试:
WriteSplit:true RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:1 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world
这时候就正常多了,大部分RTT时间都在1毫秒以下。果然禁用Nagle算法可以解决延迟问题。
如果我们不禁用nagle算法,而将writeSplit设置为false,也就是将head和body一次写入,再次运行测试(记的将setTcpNoDelay这行删除):
WriteSplit:false RTT:7 ,receive:hello world RTT:1 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world RTT:0 ,receive:hello world
结果跟禁用nagle算法的效果类似。既然这样,我们还有什么理由一定要禁用nagle算法呢?通过我在xmemcached
的
压测中的测试,启用nagle算法在小数据的存取上甚至有一定的效率优势,memcached协议本身就是个连续的请求应答的模型。上面的测试如果在
windows上跑,会发现RTT最大会在200ms以上,可见winsock的delayed ack超时是200ms。
最后一个问题,什么情况下才应该禁用nagle算法?当你的应用不是这种连续的请求——应答模型,而是需要实时地单向发送很多小数据的时候或者请求是有间
隔的,则应该禁用nagle算法来提高响应性。一个最明显是例子是telnet应用,你总是希望敲入一行数据后能立即发送给服务器,然后马上看到应答,而
不是说我要连续敲入很多命令或者等待200ms才能看到应答。
上面是我对nagle算法和delayed ack的理解和测试,有错误的地方请不吝赐教。
发表评论
-
android wifi 无线调试
2012-06-17 17:24 4426数据线丢了,不想花钱去买,在网上看了看,android手机 ... -
adb server is out of date. killing...
2012-06-17 17:20 8047在cmd窗口输入adb shell 或 adb connect ... -
JAVA 回调
2012-06-12 11:35 943熟悉MS-Windows和X Windows ... -
Class is not accessible due to restriction on required library
2012-06-03 12:13 1009How to solve This error messag ... -
Column 'id' in field list is ambiguous
2012-04-23 18:20 7662Column 'id' in field list is am ... -
java.util.ConcurrentModificationException
2012-04-01 16:24 0工作中碰到个ConcurrentModificationExc ... -
在Spring框架下获取Bean的方式总结
2012-04-01 16:23 0众所周知,Spring框架将D ... -
普通Java类获取Spring的Bean的方法
2012-04-01 16:18 1332在SSH集成的前提下。某 ... -
android 调用系统图片浏览器并返回图片路径
2012-04-01 15:56 2537调用系统图片浏览器的方法如下: Intent intent ... -
ibatis的remapResults属性合理运用
2012-03-31 15:51 1488Proper Usage of remapResults=&q ... -
response.setHeader()下载中文文件名的设置
2012-03-31 15:44 104561. HTTP消息头 (1)通用信息头 即能用于请求消息中 ... -
判断当前操作系统是不是window
2012-03-19 09:08 1612/** * 判断当前操作系统是不是window * * ... -
freemarker数字格式化
2012-03-17 16:53 3051freemarker在解析数据格式的时候,自动默认将数字按3为 ... -
数据库时客户端提示 Cannot proceed because system tables used by Event Scheduler
2012-02-29 21:13 9914在打开数据库或者用数据库管理工具(Navicat)时客户端提示 ... -
Socket用法详解
2012-02-22 16:49 1140一、构造Socket Socket的构造方法有以下几种重 ... -
在任意位置获取应用程序Context
2012-02-16 10:53 1038Android程序中访问资源时需要提供Context,一般来说 ... -
JAVA进程CPU占用率分析方法
2012-02-13 17:05 35951: 首先把JDK Linux版本上传到服务器上.2: 使 ... -
MYSQL索引优化和in or替换为union all
2012-02-07 17:08 1281使用UNION ALL代替OR,这不是绝对的。具体什么时候选择 ... -
操作cookies
2012-02-06 23:08 1027一直不是很明白有关客户端cookies的相关内容,只是觉得它可 ... -
dwr同步问题
2012-02-06 22:58 876想要异步的时候,想要同步的时候同步,在不同的情况下我们有不同的 ...
相关推荐
但当Nagle算法和delayed ACK相遇时,可能会出现一种被称为“头部-主体”阻塞的情况。 例如,在上述实验模型中,发送端先发送头部数据,然后是主体数据,接着等待接收端的应答。接收端收到头部后,由于主体数据尚未...
19、延迟确认(delayed ack) 20、拥塞控制 21、HTTP 协议基础 22、HTTP2 协议基础篇 23、HTTP2 协议进阶篇 24、Wireshark 抓取 HTTPS 包的 N 种方法 25、Select、Epoll 底层原理(上) 26、Select、Epoll 底层原理...
- Nagle算法:用于减少发送小数据包的数量,通过缓冲区数据来合并为较大的数据包进行传输。 - 延迟应答(Delayed ACK)机制:接收方延迟发送ACK以减少ACK包的数量,提高网络效率。 网络协议分析的重点总结在于...
Nagle算法在一些场景下的确能提高网络利用率、降低包处理(客户端或服务器)主机资源消耗并且工作得很好,但是在某些场景下却又弊大于利,要说清楚这个问题需要引入另一个概念,即延迟确认(Delayed ACK)。
- Nagle算法:为了减少网络中的小数据包,TCP可能会合并多个小的数据段,只在缓冲区达到一定大小或达到特定时间间隔时才发送。 - Delayed ACK(延迟确认):为了减少网络开销,TCP可能延迟发送确认,直到有数据需要...
- **Nagle算法**:鼓励发送小的数据块,减少网络中的小碎片,提高效率。 - **延迟确认(Delayed ACK)**:为了节省带宽,接收方可能会等待一段时间再发送ACK,而不是立即发送。 6. **TCP的异常处理**: - **超时...
最后,考虑到性能,TCP的缓冲区管理和Nagle算法、TCP的延迟确认(Delayed ACK)、SO_REUSEADDR选项、SO_LINGER选项以及非阻塞I/O(O_NONBLOCK标志)等都是优化网络通信效率的关键点。 通过深入学习这些概念并实践书...
此外,Nagle算法和延迟数据发送(TCP DELAYED ACK)的使用也需要根据具体场景来考虑。 七、进程与线程管理 理解进程和线程的创建、调度、同步和通信机制,对于优化多线程程序至关重要。合理设计线程池、使用锁和...
- **延迟确认计时器 (Delayed Ack Timer)**:用于控制确认消息的发送频率。 #### 问题 5-11:TCP 和 UDP 是否都需要计算往返时间 RTT? **解答要点:** - **TCP** 需要计算 RTT 来调整重传计时器和拥塞窗口大小。 -...