促进高效数据传输的TCP/IP选项(2)
[导读]不幸的是,许多常用的程序并没有考虑到以上问题。例如,EricAllman编写的sendmail就没有对其套接字设置任何选项。
另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。应用Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就让我们仔细分析下其工作原理。
假设应用程序使用sendfile()函数来转移大量数据。应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。典型情况下报头很小,而且套接字上设置了TCP_NODELAY。有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求对方确认。这样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入“塞子”)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地通过包传输出去。当数据传输完成时,最好取消TCP_CORK 选项设置给连接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络连接同等重要。总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作。示例代码如下:
intfd, on = 1;
…
/* 此处是创建套接字等操作,出于篇幅的考虑省略*/
…
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /*cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);
…
on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */
不幸的是,许多常用的程序并没有考虑到以上问题。例如,EricAllman编写的sendmail就没有对其套接字设置任何选项。Apache HTTPD是因特网上最流行的Web服务器,它的所有套接字就都设置了TCP_NODELAY选项,而且其性能也深受大多数用户的满意。这是为什么呢?答案就在于实现的差别之上。由BSD衍生的TCP/IP协议栈(值得注意的是FreeBSD)在这种状况下的操作就不同。当在TCP_NODELAY 模式下提交大量小数据块传输时,大量信息将按照一次write()函数调用发送一块数据的方式发送出去。然而,因为负责请求交付确认的记数器是面向字节而非面向包(在Linux上)的,所以引入延迟的概率就降低了很多。结果仅仅和全部数据的大小有关系。而 Linux 在第一包到达之后就要求确认,FreeBSD则在进行如此操作之前会等待好几百个包。在Linux系统上,TCP_NODELAY的效果同习惯于BSD TCP/IP协议栈的开发者所期望的效果有很大不同,而且在Linux上的Apache性能表现也会更差些。其他在Linux上频繁采用TCP_NODELAY的应用程序也有同样的问题。
相得益彰
你的数据传输并不需要总是准确地遵守某一选项或者其它选择。在那种情况下,你可能想要采取更为灵活的措施来控制网络连接:在发送一系列当作单一消息的数据之前设置TCP_CORK,而且在发送应立即发出的短消息之前设置TCP_NODELAY。把零拷贝和sendfile() 系统函数结合起来(前文有述)可以显着地提升系统整体效率并且降低CPU负载。我们采用这一技术为Swsoft’s Virtuozzo公司开发了基于名称的主机托管子系统,实践经验表明,该技术可以在装备350-MHz Pentium II CPU的PC上实现每秒9000个HTTP请求,这一成绩在以前几乎是不可能实现的。性能上的提高显而易见。






![[图]黑莓BB10系统主屏界面抢先看:MeeGo泪流满面](http://img.bbs.chinabyte.com/data/attachment/forum/201205/15/204553bbsbyasz3k3kahn6.jpg)





