我正在编写两个应用程序(在C中),这些应用程序会进行多次发送和接收调用(例如,我正在实现远程文件副本)。
我总是发送一个64字节的标题,其中包含以下消息正文的长度和一些其他信息。
在某些文件上测试我的应用程序时,我发现一些recv调用需要很长时间才能完成(大约40 ms )。使用strace我发现它首先发送一个377字节的消息体(在这种情况下它是我要复制的文件的全部内容)。
服务器应用程序开始发送大约48 us 的邮件正文。现在客户端应用程序消耗大约38 ms 来接收这些字节。
从那时起,每次接听电话消耗的时间都很长,因为它们每次都会在接收中阻塞并等待回复。
服务器的strace
[pid 27158] 1292236124.465827发送(6,“\ 0 \ 0 \ 1 \ 271 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0core.fwrite \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0“...,64,0)= 64< 0.000031>
[pid 27158] 1292236124.466074发送(6,“\ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0“......,377,0)= 377< 0.000048>
客户的strace
[pid 27159] 1292236124.466364 recv(4,“\ 0 \ 0 \ 1 \ 271 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0core.fwrite \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0“...,64,0)= 64< 0.000027>
[pid 27159] 1292236124.466597 recv(4,“\ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 10 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0“......,377,0)= 377< 0.037456>
这个问题确实给我带来了困难,因为我不明白为什么客户端的接听电话需要这么多时间。
任何提示都将受到高度赞赏。
答案 0 :(得分:7)
听起来像Nagle's algorithm给我。你没有提交足够的数据,所以它会延迟一段时间,以防有更多的数据出现。您可以通过套接字选项禁用它,然后重试。
答案 1 :(得分:0)
是的,这肯定是Nagle的算法。我建议你阅读它,因为如果你发送大块数据,它应该在它有“大量”数据时立即发送。没有自己阅读,我不完全确定“有多少”,但它可能是可配置的。
但是,如果您在每个连接上执行一个文件,那么如果您一次性发送标题和内容,那么您应该不会遇到小文件问题 - 无论如何您应该这样做以提高吞吐量。正如李杰所说(或多或少)“吞吐量是游戏的名称”。您需要将文件数据与标头一起缓冲。这是我可能会推荐多线程的少数情况之一 - 一个线程从文件填充缓冲区,另一个线程从缓冲区加载套接字。您需要使用互斥锁保护缓冲区和关联变量,我也建议使用条件变量。文件读取线程在添加了更多数据时发出信号,并在缓冲区已满时等待,并且套接字写入线程在读取时发出信号并在缓冲区为空时等待。写入64字节标题后,文件读取线程不应发出信号。让它首先加载一个完整的数据缓冲区。
您也可以尝试使用两个缓冲区并使用它们来减少互斥锁定延迟。如果你做对了,文件读取线程将写入缓冲区A,而套接字写入线程正在读取缓冲区B,反之亦然,线程将更少地等待互斥锁。
即使采用这样的策略,仍然值得禁用Nagle的算法。除了形成任何东西之外,如果您的代码被设计为无论如何都要避免大量的小数据包,那么Nagle的算法是多余的。