我已经写了一个HTTP代理,做了一些在这里不相关的东西,但是它增加了客户端的大量服务(600us没有代理服务器,60000us服务器)。 我想我已经find了大部分时间来自哪里 – 在我的代理完成发回给客户和客户完成接收之间。 目前,服务器,代理和客户端在同一主机上运行,使用localhost作为地址。
一旦代理完成发送(至less从send()返回),我打印gettimeofday的结果给出绝对时间。 当我的客户端收到,它打印gettimeofday的结果。 既然他们都在同一个主机上,这应该是准确的。 所有send()调用都没有标志,所以它们被阻塞。 两者之间的差异约为40000us。
代理的侦听客户端连接的套接字使用提示AF_UNSPEC,SOCK_STREAM和AI_PASSIVE进行设置。 据推测,来自accept()的套接字将具有相同的参数?
如果我正确地理解了这一切,那么Apache就会设法在600us中做所有事情(包括导致这个40000us延迟的任何事情)。 有人可以提出什么可能造成这个? 我已经尝试设置TCP_NODELAY选项(我知道我不应该,只是为了看看是否有所作为),完成发送和完成接收之间的延迟下降,我忘了数字,但<1000US。
这一切都在Ubuntu Linux 2.6.31-19上。 谢谢你的帮助
对于同一主机上的客户端,代理和源服务器的代理,您无法真正做出有意义的性能测量。
将它们全部放在网络上的不同主机上。 使用真正的硬件机器,或专门的硬件测试系统(如思博伦)。
你的方法是没有意义的。 无论如何,没有人在其服务器上有600us的延迟。 在同一台主机上运行所有的任务会产生争用和完全不回报的网络环境。
40ms是Linux上的TCP ACK延迟,这表明您可能遇到延迟的ack和Nagle算法之间的不良交互。 解决这个问题的最好方法是在等待响应之前,使用单个调用send()
或sendmsg()
发送所有数据。 如果这是不可能的,那么某些TCP套接字选项,包括TCP_QUICKACK
(在接收端), TCP_CORK
(发送端)和TCP_NODELAY
(发送端)可以提供帮助,但是如果使用不当,也可能会受到伤害。 TCP_NODELAY
简单地禁用了Nagle算法,并且是套接字上的一次性设置,而另外两个设置必须在连接生命周期的适当时间进行设置,因此可能会更棘手。
介绍:
我已经称赞mark4o为降低延迟的一般问题提供了真正正确的答案。 我想根据它如何帮助解决我的延迟问题来翻译答案,因为我认为这将是大多数人来这里寻找的答案。
回答:
在一个实时的网络应用程序(如多人游戏)中尽快获取节点之间的短消息是至关重要的,关闭。 在大多数情况下,这意味着将“不延迟”标志设置为真。
免责声明:
虽然这可能不能解决OP的具体问题,但是大多数来这里的人可能会在寻找这个解决延迟问题的一般问题的答案。
ANECDOTAL BACK-STORY:
我的游戏运行良好,直到我添加了代码来分别发送两条消息,但在执行时间它们彼此非常接近。 突然间,我得到了250毫秒的额外延迟。 由于这是一个更大的代码更改的一部分,我花了两天的时间试图找出我的问题是什么。 当我把这两个信息合并成一个时,问题就消失了。 逻辑导致我mark4o的职位,所以我把.Net套接字成员“NoDelay”设置为true,我可以发送尽可能多的消息在一排,我想要的。
从例如RedHat文档:
每个发送的数据包都要求较低延迟的应用程序应在启用了TCP_NODELAY的套接字上运行。 可以通过使用套接字API的setsockopt命令启用它:
int one = 1; setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
为了有效地使用它,应用程序必须避免做小的,逻辑上相关的缓冲区写操作。 由于TCP_NODELAY已启用,这些小写操作将使TCP将这些多个缓冲区作为单个数据包发送,从而导致整体性能较差。
在你的情况下,那40毫秒可能只是一个调度程序的时间量。 换句话说,这就是你的系统回到其他任务需要多长时间。 尝试一个真正的网络,你会得到一个完全不同的图片。 如果你有一个多核的机器,在Virtualbox或其他虚拟机上使用虚拟操作系统实例会给你一个更好的想法。
对于TCP代理来说,在LAN-side上增加TCP初始化窗口大小似乎是谨慎的,正如在linux-netdev和/中所讨论的那样。 最近。
http://www.amailbox.org/mailarchive/linux-netdev/2010/5/26/6278007
包括谷歌关于这个话题的论文,
http://www.google.com/research/pubs/pub36640.html
还有一个Google的IETF草案,
http://zinfandel.levkowetz.com/html/draft-ietf-tcpm-initcwnd-00
对于Windows,我不确定设置TCP_NODELAY是否有帮助。 我尝试过,但延迟还是很糟糕。 有人建议我尝试UDP,那就是诀窍。
一些UDP的复杂的例子并不适合我,但我碰到了一个简单的例子,它做了诡计…
#include <Winsock2.h> #include <WS2tcpip.h> #include <system_error> #include <string> #include <iostream> class WSASession { public: WSASession() { int ret = WSAStartup(MAKEWORD(2, 2), &data); if (ret != 0) throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed"); } ~WSASession() { WSACleanup(); } private: WSAData data; }; class UDPSocket { public: UDPSocket() { sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock == INVALID_SOCKET) throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket"); } ~UDPSocket() { closesocket(sock); } void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0) { sockaddr_in add; add.sin_family = AF_INET; add.sin_addr.s_addr = inet_addr(address.c_str()); add.sin_port = htons(port); int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&add), sizeof(add)); if (ret < 0) throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed"); } void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0) { int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&address), sizeof(address)); if (ret < 0) throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed"); } sockaddr_in RecvFrom(char* buffer, int len, int flags = 0) { sockaddr_in from; int size = sizeof(from); int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&from), &size); if (ret < 0) throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed"); // make the buffer zero terminated buffer[ret] = 0; return from; } void Bind(unsigned short port) { sockaddr_in add; add.sin_family = AF_INET; add.sin_addr.s_addr = htonl(INADDR_ANY); add.sin_port = htons(port); int ret = bind(sock, reinterpret_cast<SOCKADDR *>(&add), sizeof(add)); if (ret < 0) throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed"); } private: SOCKET sock; };
服务器
#define TRANSACTION_SIZE 8 static void startService(int portNumber) { try { WSASession Session; UDPSocket Socket; char tmpBuffer[TRANSACTION_SIZE]; INPUT input; input.type = INPUT_MOUSE; input.mi.mouseData=0; input.mi.dwFlags = MOUSEEVENTF_MOVE; Socket.Bind(portNumber); while (1) { sockaddr_in add = Socket.RecvFrom(tmpBuffer, sizeof(tmpBuffer)); ...do something with tmpBuffer... Socket.SendTo(add, data, len); } } catch (std::system_error& e) { std::cout << e.what(); }
客户
char *targetIP = "192.168.1.xxx"; Socket.SendTo(targetIP, targetPort, data, len);