我已经编写了一个HTTP代理,它做了一些与此无关的东西,但它增加了客户的服务时间(600us没有代理,60000us)。我想我已经找到了大部分时间来自哪里 - 在我的代理完成发送回客户端和客户完成接收之间。目前,服务器,代理和客户端在同一主机上运行,使用localhost作为地址。
一旦代理完成发送(至少从send()返回后),我打印gettimeofday的结果,给出一个绝对时间。当我的客户收到时,它会输出gettimeofday的结果。由于它们都在同一主机上,因此这应该是准确的。所有send()调用都没有标志,因此它们是阻塞的。两者之间的差异大约是40000us。
使用提示AF_UNSPEC,SOCK_STREAM和AI_PASSIVE设置侦听客户端连接的代理套接字。推测来自accept()的套接字将具有相同的参数?
如果我正确地理解了这一切,那么Apache设法在600us内完成所有工作(包括导致40000us延迟的相应内容)。任何人都可以提出可能导致此问题的原因吗?我已经尝试设置TCP_NODELAY选项(我知道我不应该,只是为了看它是否有所不同)并且完成发送和完成接收之间的延迟时间正确,我忘记了数字但是<1000us。
这一切都在Ubuntu Linux 2.6.31-19上。谢谢你的帮助
答案 0 :(得分:37)
40ms是Linux上的TCP ACK延迟,表示您可能在延迟的ack和Nagle algorithm之间遇到了错误的交互。解决此问题的最佳方法是在等待响应之前,通过一次调用send()
或sendmsg()
发送所有数据。如果不可能,则某些TCP socket options包括TCP_QUICKACK
(在接收方),TCP_CORK
(发送方)和TCP_NODELAY
(发送方)可以提供帮助,但可以如果使用不当也会受伤。 TCP_NODELAY
只是禁用Nagle算法,并且是套接字上的一次性设置,而其他两个必须在连接的生命周期中的适当时间设置,因此可能更难以使用。
答案 1 :(得分:5)
您无法在同一主机上使用客户端,代理和源服务器的代理上进行有意义的性能测量。
将它们全部放在网络上的不同主机上。使用真正的硬件机器或专用硬件测试系统(例如思博伦)。
你的方法没有意义。无论如何,在实践中没有人有600us的原始服务器延迟。在同一主机上运行所有任务会产生争用和完全不可靠的网络环境。
答案 2 :(得分:4)
引言:
我已经赞扬了mark4o对降低延迟的一般问题的真正正确答案。我想根据它如何帮助解决我的延迟问题来解释答案,因为我认为这将是大多数人来这里寻找的答案。
解答:
在实时网络应用程序(例如多人游戏)中,尽可能快地在节点之间获取短消息至关重要,TURN NAGLE OFF。在大多数情况下,这意味着将“无延迟”标志设置为true。
声明:
虽然这可能无法解决OP特定的问题,但是大多数来到这里的人可能会在寻找他们潜在问题的一般问题的答案。
ANECDOTAL BACK-STORY:
我的游戏运行正常,直到我添加了代码才能分别发送两条消息,但它们在执行时间上非常接近。突然间,我得到了250ms的额外延迟。由于这是更大代码更改的一部分,我花了两天时间试图弄清楚我的问题是什么。当我将这两个消息合并为一个时,问题就消失了。 Logic把我带到了mark4o的帖子,所以我将.Net套接字成员“NoDelay”设置为true,我可以根据需要连续发送多条消息。
答案 3 :(得分:3)
例如RedHat文档:
在发送的每个数据包上需要较低延迟的应用程序应在启用了TCP_NODELAY的套接字上运行。它可以通过带有套接字API的setsockopt命令启用:
int one = 1;
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
为了有效地使用它,应用程序必须避免执行与逻辑相关的小型缓冲区写入。由于TCP_NODELAY已启用,因此这些小写操作会使TCP将这些多个缓冲区作为单个数据包发送,从而导致整体性能下降。
答案 4 :(得分:1)
在你的情况下,40ms可能只是一个调度时间量。换句话说,这是你的系统回到其他任务需要多长时间。在真实的网络上试一试,你会得到完全不同的画面。如果你有一台多核机器,在Virtualbox或其他VM中使用虚拟操作系统实例可以让你更好地了解真正发生的事情。
答案 5 :(得分:1)
对于TCP代理,如果在linux-netdev和/上讨论,在LAN端增加TCP初始窗口大小似乎是明智的。最近。
http://www.amailbox.org/mailarchive/linux-netdev/2010/5/26/6278007
包括Google主题的论文,
http://www.google.com/research/pubs/pub36640.html
还有谷歌的IETF草案,
http://zinfandel.levkowetz.com/html/draft-ietf-tcpm-initcwnd-00
答案 6 :(得分:0)
对于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);