2010-02-12 92 views
2

我寫了一個HTTP代理,它做了一些與這裏無關的東西,但它增加了客戶端的大量服務(600us沒有代理服務器,60000us服務器)。我想我已經找到了大部分時間來自哪裏 - 在我的代理完成發回客戶端和客戶端完成接收之間。目前,服務器,代理和客戶端在同一臺主機上運行,​​使用本地主機作爲地址。(How)我可以減少套接字延遲嗎?

一旦代理完成發送(一旦它至少從send()返回),我打印gettimeofday的結果給出絕對時間。當我的客戶端收到時,它會打印gettimeofday的結果。由於它們都在同一臺主機上,因此這應該是準確的。所有send()調用都沒有標誌,所以它們被阻塞。兩者之間的差異約爲40000us。

用於監聽客戶端連接的代理套接字使用提示AF_UNSPEC,SOCK_STREAM和AI_PASSIVE進行設置。據推測來自accept()的套接字將具有相同的參數?

如果我正確理解了所有這些,Apache會設法在600us中做所有事情(包括導致這個40000us延遲的任何事情)。任何人都可以提出什麼可能導致這種情況?我已經嘗試設置TCP_NODELAY選項(我知道我不應該這樣做,只是爲了看看它是否有所不同),並且發送完成和接收完成之間的延遲正好相反,我忘記了數字,但是< 1000us。

這都是在Ubuntu Linux 2.6.31-19上。感謝您的幫助

回答

5

您無法在同一主機上的客戶端,代理和原始服務器上對代理進行有意義的性能測量。

將它們全部放置在網絡上的不同主機上。全部使用真正的硬件機器,或專門的硬件測試系統(例如思博倫)。

您的方法沒有意義。無論如何,沒有人在其服務器上有600us的延遲。在同一臺主機上運行所有任務會產生爭用和完全不迴歸的網絡環境。

+0

非常感謝您的回答。我不認爲這會有很大的不同。我已經完成了你所建議的任務,並且在不同的主機上運行了每一個主機,並且現在的服務時間與沒有代理服務器的時間現在幾乎沒有區別。 – Ray2k 2010-02-12 14:04:55

+0

「沒有人有600us的延遲到他們的原始服務器」 - 這是真的嗎?正常的以太網具有大約0.5ms的延遲(使用'ping'嘗試)。 – nh2 2015-03-15 01:21:40

+0

@ nh2,對,所以從那裏開始,然後添加大多數服務器的延遲,我認爲你有1ms +。 – 2015-05-07 03:46:38

3

從例如RedHat文檔:

要求每個發送數據包的延遲時間較短的應用程序應在啓用了TCP_NODELAY的套接字上運行。它可以通過與插座API的調用setsockopt命令啓用:

int one = 1; 
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); 

對於這個被有效使用,應用程序必須避免做小,邏輯相關的緩衝區寫入。由於TCP_NODELAY已啓用,因此這些小型寫入將使TCP將這些多個緩衝區作爲單獨的數據包發送,從而導致整體性能較差。

+2

順便說一句,有可能有你的蛋糕,在這種情況下也吃,你可以離開內格爾的啓用然後當你將所有數據寫入你可能會寫一段時間的套接字時,禁用Nagle's,send()0在套接字上測試,然後再次重新啓用Nagle。 send()將強制任何待處理的數據立即發送。 – 2010-02-12 16:43:33

1

在你的情況下,那40ms可能只是一個調度程序的時間量。換句話說,這就是您的系統需要多長時間才能完成其他任務。嘗試一個真實的網絡,你會得到一個完全不同的圖片。如果您有多核機器,在Virtualbox或其他虛擬機中使用虛擬操作系統實例會讓您更好地瞭解真正發生的事情。

+0

謝謝,我已經完成了你所建議的(真實網絡),現在正在獲得預期的結果。 – Ray2k 2010-02-12 14:06:07

33

40ms是Linux上的TCP ACK延遲,表示您很可能在延遲的Ack和Nagle algorithm之間遇到了不良的交互。解決此問題的最佳方法是在等待響應之前,使用一次調用將所有數據發送至send()sendmsg()。如果這是不可能的,那麼某些TCP socket options包括TCP_QUICKACK(在接收端),TCP_CORK(發送端)和TCP_NODELAY(發送端)可以提供幫助,但是如果使用不當可能會造成傷害。 TCP_NODELAY只是禁用了Nagle算法,並且是套接字上的一次性設置,而其他兩個設置必須在連接生命週期的適當時間進行設置,因此可能會更棘手。

+5

對於「如何減少套接字延遲?」這個問題,這個答案是最好的答案。 - 這是問題。不要提出解決OP特定問題的答案,這不是讓這個網站如此強大的原因。這個網站很棒,因爲我們可以快速找到解決我們問題的方法。所以如果你是一個真正的StackOverflow'er,這種答案是你加分的(至少是恕我直言,哈哈)。 – Jin 2012-01-15 06:04:36

+0

謝謝!使用setsockopt()設置TCP_NODELAY解決了我的問題。 – Adam 2018-03-07 16:56:28

1

對於TCP代理來說,在LAN-side netdev和/或上討論增加TCP初始窗口大小似乎是謹慎的。最近。

http://www.amailbox.org/mailarchive/linux-netdev/2010/5/26/6278007

http://developers.slashdot.org/story/10/11/26/1729218/Google-Microsoft-Cheat-On-Slow-Start-mdash-Should-You

包括關於谷歌的話題紙,

http://www.google.com/research/pubs/pub36640.html

而一個IETF草案還通過谷歌,

http://zinfandel.levkowetz.com/html/draft-ietf-tcpm-initcwnd-00

4

簡介:

我已經稱讚mark4o爲降低延遲的一般問題的真正正確答案。我想根據它如何幫助解決我的延遲問題來翻譯答案,因爲我認爲這將成爲大多數人來這裏尋找的答案。

解答:

在實時網絡應用程序(如多玩家遊戲),其中得到的節點之間的短消息儘可能快地是至關重要的,TURN內格爾OFF。在大多數情況下,這意味着將「無延遲」標誌設置爲真。

免責聲明:

雖然這可能不是解決OP具體問題,誰到這裏大多數人可能會尋找這個答案,他們的延遲問題的一般問題。

傳聞背景故事:

我的比賽做得很好,直到我說代碼單獨發送兩條消息,但他們是在執行時間上非常接近對方。突然間,我得到了250毫秒的額外延遲。由於這是更大的代碼更改的一部分,我花了兩天的時間試圖弄清楚我的問題是什麼。當我把這兩個信息合併成一個時,問題就消失了。 Logic將我引導至mark4o的帖子,因此我將.Net套接字成員「NoDelay」設置爲true,並且我可以根據需要發送儘可能多的消息。

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);