還是我必須在應用程序級別實現它?有沒有辦法阻止一個套接字send(),直到我們得到該包的確認?
回答
爲什麼不使用阻塞套接字?
這可能有點過時,但這裏是關於阻塞/非阻塞和重疊IO的一些解釋。
http://support.microsoft.com/kb/181611
如果我們知道你正在使用的語言和操作系統,順便說一句,爲了更好地展示代碼段這將有助於。
數據包的確認位於傳輸層(遠低於應用層)。你甚至不能保證你的整個緩衝區都屬於它自己的網絡數據包。你在做什麼?
如果你在談論TCP,那麼沒有 - 我看到的套接字API允許你這樣做。
如果您需要確保另一端已收到(並可能處理)您的數據,則需要在應用程序協議中實施ack。
如果使用setsockopt()
降低SO_SNDBUF
只允許大的值足夠發送一個數據包,則該插座上的下一個send()
應該阻止,直到前面的數據包被確認。但是,根據tcp(7)
,套接字緩衝區大小必須在listen()
/connect()
之前設置。
使用TCP的關鍵在於隱藏應用程序中的單個ACK。如果您需要檢測每個ACK,則使用UDP或IP實現您自己的協議。 TCP可能是一個矯枉過正的問題。或者你可以上傳堆棧並使用像HTTP這樣的協議作爲傳輸。
TCP通常會要求您在應用程序級別同步接收方和發送方。單獨調整SO_SNDBUF
或單獨TCP_NODELAY
的組合不可能完全解決問題。這是因爲,可以「在飛行中」 send()
之前將阻塞的數據量是或多或少等於的總和:在發送側的發送緩衝器中的數據,包括小數據片段通過被延遲
- Nagle's algorithm,
- 未確認的正在傳輸的數據包中攜帶的數據量,其數量因congestion window(
CWIN
)和接收窗口(RWIN
)的大小而異。隨着TCP在慢啓動,擁塞避免,快速恢復和快速重傳模式之間轉換,TCP發送方不斷調整擁塞窗口大小以適應網絡條件。並且, - 接收方的接收緩衝區中的數據,接收方的TCP堆棧已經發送了一個
ACK
,但應用程序尚未看到。
要說它的另一種方式,接收器將停止從插座,send()
只會當塊讀數據後:
- 接收者的TCP接收緩衝區填滿和TCP停止
ACK
ING, - 的發送方將發送的數據發送到擁塞或接收窗口限制,並且
- 發送方的TCP發送緩衝區填充或發送方應用程序請求發送緩衝區刷新。
在TCP中使用的算法的目標是創建流動的字節流而不是數據包序列的效果。總的來說,它試圖儘可能隱藏傳輸被量化成數據包的事實,而大多數套接字API反映了這一點。其中一個原因是套接字可能無法在頂級TCP(甚至是IP)上實現:考慮一個使用相同API的Unix域套接字。
試圖依賴於TCP的應用程序行爲的底層實現細節通常是不可取的。堅持在應用層進行同步。
如果在執行同步的情況下延遲是一個問題,那麼您可能還需要閱讀有關interactions between Nagle's algorithm and delayed ACK
的問題,這些問題可能會在某些情況下引入不必要的延遲。
根據定義,在套接字發送緩衝區中不能有任何更多的數據,因此將兩者相加無意義。接收器的套接字接收緩衝區中的數據未處於運行狀態。 (2)單獨給出了飛行中的數據總量。 – EJP 2016-02-24 10:58:35
幾周前我在實施VoIP服務器時也遇到了同樣的問題。花了幾天後,我可以想出一個解決方案。和其他人一樣,沒有任何直接的系統調用來完成這項工作。相反,
- 可以檢查我們發送的數據包,
TCP_INFO
選項後收到的ACK
。 - 如果我們還沒有收到
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++的新手,這是我能想到的。
感謝您的好回答TMtech :) – 2016-04-20 23:38:05
您的回答指出:「如果連接打開一半,您將永遠不會收到ACK而導致無限循環。」如果連接打開一半,爲什麼不收到ACK? ACK在兩個流上獨立出現。另外,你說,「在發送一個包含TCP_INFO選項的包之後。」我不認爲TCP_INFO發送任何數據包,但它僅僅是一種不可移植的方式來查詢來自本地內核的未完成數據包的數量。 – kgibm 2018-02-28 21:45:32
@kgibm如果兩個流獨立出現ACK,那麼發送者知道接收者已收到發送的數據包?注意這是TCP,而不是UDP。如果您需要更多有關協議的信息,請參閱以下鏈接 http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/ – TMtech 2018-03-02 09:23:26
- 1. 有沒有辦法阻止主線程,直到完成一個迴轉工作?
- 2. 有沒有辦法阻止生成CR字符的直線?
- 3. 有沒有辦法得到一個直接指向通過JNI的Java數組?
- 4. 套接字接受()阻塞,直到我按下一個鍵
- 5. 有沒有辦法確定如何套接字連接?
- 6. 有沒有辦法阻止拖放到iOS 11中的WKWebView?
- 7. 有沒有辦法讓Excel VBA發送數據到套接字?
- 8. 有沒有辦法阻止代理?
- 9. 有沒有辦法獲得unix套接字連接的另一端的uid
- 10. 有沒有辦法阻止打印出一部分字符串?
- 11. 有沒有辦法阻止打開時看到python窗口?
- 12. Maven發佈:有沒有辦法阻止模塊上傳到Nexus?
- 13. 有沒有辦法阻止Eclipse將文件夾移動到另一個拖放?
- 14. 有沒有辦法將升壓信號直接連接到另一個信號?
- 15. 有沒有辦法阻止Valgrind發現第一個錯誤?
- 16. 有沒有辦法阻止一個SearchView小部件崩潰
- 17. e.preventDefault - 有沒有辦法做到默認?
- 18. 有沒有辦法阻止按國家訪問我的網站?
- 19. 有沒有辦法將perl腳本的套接字連接到PHP服務器?
- 20. AngularJS阻止所有路由,直到認證承諾得到解決
- 21. 有沒有辦法直接發送python輸出到剪貼板?
- 22. 有沒有辦法阻止IE9中的這個JavaScript錯誤
- 23. 有沒有辦法來阻止這個FluentValidation上的stackoverflow?
- 24. 有沒有辦法阻止ie7加載一些內聯JavaScript?
- 25. WebAudio:有沒有辦法確定節點是否連接到另一個節點?
- 26. 有沒有辦法阻止用這個javascript/jquery提交表單?
- 27. 我沒有得到我的包在MainActivity
- 28. 有沒有辦法得到沒有CONNECTION變量的mysqli_error?
- 29. 有沒有辦法得到沒有文本的UIDatePickerView?
- 30. 有沒有辦法得到一個隱含的清單以一個特點
它不會幫助。阻塞套接字塊,直到您成功地將數據傳遞到OS網絡堆棧,直到您收到對方的確認信號爲止......我在Linux btw上使用C ... – anon 2009-08-14 18:05:42
但是,您可以阻止,直到您收到響應,這可能是確認。 – 2009-08-14 21:35:20
看來,即使阻塞寫入可以成功返回之前收到ACK。 POSIX說,「成功完成一個呼叫發送()不保證消息的傳送。」 – kgibm 2018-02-28 21:48:21