2009-08-14 33 views

回答

0

爲什麼不使用阻塞套接字?

這可能有點過時,但這裏是關於阻塞/非阻塞和重疊IO的一些解釋。

http://support.microsoft.com/kb/181611

如果我們知道你正在使用的語言和操作系統,順便說一句,爲了更好地展示代碼段這將有助於。

+3

它不會幫助。阻塞套接字塊,直到您成功地將數據傳遞到OS網絡堆棧,直到您收到對方的確認信號爲止......我在Linux btw上使用C ... – anon 2009-08-14 18:05:42

+0

但是,您可以阻止,直到您收到響應,這可能是確認。 – 2009-08-14 21:35:20

+0

看來,即使阻塞寫入可以成功返回之前收到ACK。 POSIX說,「成功完成一個呼叫發送()不保證消息的傳送。」 – kgibm 2018-02-28 21:48:21

1

數據包的確認位於傳輸層(遠低於應用層)。你甚至不能保證你的整個緩衝區都屬於它自己的網絡數據包。你在做什麼?

3

如果你在談論TCP,那麼沒有 - 我看到的套接字API允許你這樣做。

如果您需要確保另一端已收到(並可能處理)您的數據,則需要在應用程序協議中實施ack。

0

如果使用setsockopt()降低SO_SNDBUF只允許大的值足夠發送一個數據包,則該插座上的下一個send()應該阻止,直到前面的數據包被確認。但是,根據tcp(7),套接字緩衝區大小必須在listen()/connect()之前設置。

+0

來源訣竅是知道單個數據包(即MTU)的確切大小。無論哪種方式這種方法可能會導致性能下降,但這可能無關緊要的OP。 – mox1 2009-08-14 20:30:45

+0

如果應用程序發送的消息是小的固定大小,例如512字節,並且一次只發送一個,則不需要知道MTU。 – mark4o 2009-08-14 20:38:49

+0

直到最終出現擁塞的情況,操作系統數字「讓我們嘗試發送更小的段」。 – nos 2009-08-14 21:08:09

0

使用TCP的關鍵在於隱藏應用程序中的單個ACK。如果您需要檢測每個ACK,則使用UDP或IP實現您自己的協議。 TCP可能是一個矯枉過正的問題。或者你可以上傳堆棧並使用像HTTP這樣的協議作爲傳輸。

6

TCP通常會要求您在應用程序級別同步接收方和發送方。單獨調整SO_SNDBUF或單獨TCP_NODELAY的組合不可能完全解決問題。這是因爲,可以「在飛行中」 send()之前將阻塞的數據量是或多或少等於的總和:在發送側的發送緩衝器中的數據,包括小數據片段通過被延遲

  1. Nagle's algorithm,
  2. 未確認的正在傳輸的數據包中攜帶的數據量,其數量因congestion windowCWIN)和接收窗口(RWIN)的大小而異。隨着TCP在慢啓動,擁塞避免,快速恢復和快速重傳模式之間轉換,TCP發送方不斷調整擁塞窗口大小以適應網絡條件。並且,
  3. 接收方的接收緩衝區中的數據,接收方的TCP堆棧已經發送了一個ACK,但應用程序尚未看到。

要說它的另一種方式,接收器將停止從插座,send()只會當塊讀數據後:

  1. 接收者的TCP接收緩衝區填滿和TCP停止ACK ING,
  2. 的發送方將發送的數據發送到擁塞或接收窗口限制,並且
  3. 發送方的TCP發送緩衝區填充或發送方應用程序請求發送緩衝區刷新。

在TCP中使用的算法的目標是創建流動的字節流而不是數據包序列的效果。總的來說,它試圖儘可能隱藏傳輸被量化成數據包的事實,而大多數套接字API反映了這一點。其中一個原因是套接字可能無法在頂級TCP(甚至是IP)上實現:考慮一個使用相同API的Unix域套接字。

試圖依賴於TCP的應用程序行爲的底層實現細節通常是不可取的。堅持在應用層進行同步。

如果在執行同步的情況下延遲是一個問題,那麼您可能還需要閱讀有關interactions between Nagle's algorithm and delayed ACK的問題,這些問題可能會在某些情況下引入不必要的延遲。

+0

根據定義,在套接字發送緩衝區中不能有任何更多的數據,因此將兩者相加無意義。接收器的套接字接收緩衝區中的數據未處於運行狀態。 (2)單獨給出了飛行中的數據總量。 – EJP 2016-02-24 10:58:35

5

幾周前我在實施VoIP服務器時也遇到了同樣的問題。花了幾天後,我可以想出一個解決方案。和其他人一樣,沒有任何直接的系統調用來完成這項工作。相反,

  1. 可以檢查我們發送的數據包,TCP_INFO選項後收到的ACK
  2. 如果我們還沒有收到ACK,請等待幾毫秒後再檢查一次。

這可能會一直持續到超時。你必須實現它作爲發送()調用的包裝函數。 您將需要tcp_info結構從<netinet/tcp.h>。這是用於保存關於您的tcp連接的信息的數據結構

這裏是僞代碼

int blockingSend(const char *msg, int msglen, int timeout) { 

    std::lock_guard<std::mutex> lock(write_mutx); 

    int sent = send(sock_fd, msg, msglen, 0); 

    tcp_info info; 
    auto expireAt = chrono::system_clock::now() + chrono::milliseconds(timeout); 
    do { 
     this_thread::sleep_for(milliseconds(50)); 
     getsockopt(sock_fd,SOL_TCP, TCP_INFO, (void *) &info, sizeof(info)); 

     //wait till all packets acknowledged or time expires 
    } while (info.tcpi_unacked > 0 && expireAt > system_clock::now()); 

    if(info.tcpi_unacked>0) { 
     cerr << "no of unacked packets :" << info.tcpi_unacked << endl; 
     return -1; 
    } 
    return sent; 
} 

這裏tcpi_unacked成員保存數據包的連接未確認的數量。如果在send()調用後不久讀取它,它將包含數量爲的未發送的數據包,其數量等於發送的數據包數量。隨着時間的推移,沒有得到的數據包將遞減到零。因此,您需要定期檢查tcpi_unacked的值,直到達到零。如果連接打開一半,則永遠不會收到ACK,同時導致無限循環。對於這種情況,您可能需要添加上面實現的超時機制。

即使這個問題很久以前就被問到了,這個答案可能會幫助一些面臨同樣問題的人。我必須指出,這個解決方案可能會有更準確的解決方案。由於我是系統編程和C/C++的新手,這是我能想到的。

+0

感謝您的好回答TMtech :) – 2016-04-20 23:38:05

+0

您的回答指出:「如果連接打開一半,您將永遠不會收到ACK而導致無限循環。」如果連接打開一半,爲什麼不收到ACK? ACK在兩個流上獨立出現。另外,你說,「在發送一個包含TCP_INFO選項的包之後。」我不認爲TCP_INFO發送任何數據包,但它僅僅是一種不可移植的方式來查詢來自本地內核的未完成數據包的數量。 – kgibm 2018-02-28 21:45:32

+0

@kgibm如果兩個流獨立出現ACK,那麼發送者知道接收者已收到發送的數據包?注意這是TCP,而不是UDP。如果您需要更多有關協議的信息,請參閱以下鏈接 http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/ – TMtech 2018-03-02 09:23:26

相關問題