當應用程序調用Send之後怎麼判斷對方是否成功接收?

找了好多答案,都是只能從應用層發送一個確認。
TCP已經使用一些機制可靠的發送過去,為何傳輸層不向上提供一個「ACK」,還要讓開發者從應用層發送一個「ACK",這樣TCP不是更慢了嗎?
另外UDP的Send recv又怎麼判斷對方是否成功接收?
不知道問得準不準確,如果有不對的地方,請大家指正。


你問得很準確,答案也很明確,就是要應用層發送一個確認。
TCP 的 ACK 表示對方的協議棧已經收到了你發的數據,不代表對方的應用程序收到了你發的消息。
對方的應用程序可能死鎖或者阻塞,不會去調用 recv(),那麼你發的數據就堆積在對方協議棧的接收緩衝區里了。
這種情況你認為算是對方收到還是沒有收到消息呢?


首先,TCP是可靠的數據連接,send過去的數據,一定會被對方接收到,除非連接斷開。
TCP和上層應用之間是網路層和應用層之間的關係,TCP收到的ACK只能表明對端TCP收到了相應的數據在內核緩存中,對端應用是否讀取到了這條消息,是不能判斷的,這點贊同 @陳碩的答案。
------------------------------------------------------------------------------------
題主可能對TCP中的send recv 和應用層的發送與消息接收之間的關係不是很清楚

先明確一個概念:每個TCP socket在內核中都有一個發送緩衝區和一個接收緩衝區,TCP的全雙工的工作模式以及TCP的滑動窗口便是依賴於這兩個獨立的buffer以及此buffer的填充狀態。接收緩衝區把數據緩存入內核,應用進程一直沒有調用read進行讀取的話,此數據會一直緩存在相應 socket的接收緩衝區內。再啰嗦一點,不管進程是否讀取socket,對端發來的數據都會經由內核接收並且緩存到socket的內核接收緩衝區之中。 read所做的工作,就是把內核緩衝區中的數據拷貝到應用層用戶的buffer裡面,僅此而已。進程調用send發送的數據的時候,最簡單情況(也是一般情況),將數據拷貝進入socket的內核發送緩衝區之中,然後send便會在上層返回(也就是說send的返回值不能表明數據已經發送到了對端並被接收)。換句話說,send返回之時,數據不一定會發送到對端去(和 write寫文件有點類似),send僅僅是把應用層buffer的數據拷貝進socket的內核發送buffer中。後續我會專門用一篇文章介紹 read和send所關聯的內核動作。每個UDP socket都有一個接收緩衝區,沒有發送緩衝區,從概念上來說就是只要有數據就發,不管對方是否可以正確接收,所以不緩衝,不需要發送緩衝區。

接收緩衝區被TCP和UDP用來緩存網路上來的數據,一直保存到應用進程讀走為止。對於TCP,如果應用進程一直沒有讀取,buffer滿了之後,發生的動作是:通知對端TCP協議中的窗口關閉。這個便是滑動窗口的實現。保證TCP套介面接收緩衝區不會溢出,從而保證了TCP是可靠傳輸。因為對方不允許發出超過所通告窗口大小的數據。 這就是TCP的流量控制,如果對方無視窗口大小而發出了超過窗口大小的數據,則接收方TCP將丟棄它。 UDP:當套介面接收緩衝區滿時,新來的數據報無法進入接收緩衝區,此數據報就被丟棄。UDP是沒有流量控制的;快的發送者可以很容易地就淹沒慢的接收者,導致接收方的UDP丟棄數據報。

綜上 :對方接收成功後給返回一個消息,發送方收到這個消息後就認為對方接收成功。

發送端是不管對方接收是否成功的,你是無法判斷的。只有讓對方接受成功後,給你發一個通知
(TCP只要發送出去了,就已經發送到對方環境中了,如果是有操作的請求,那應該等待返回處理成功消息,如果是QQ這樣的應用,發送出去的消息要保證對方收到,對方客戶端收到後會給回應我收到了某某的哪條消息,TCP全部發送到伺服器之後就確認發送過去了,但是應用層可以給個回應),已讀這類功能肯定就是接受方主動發送的消息,告知我讀了


展開陳碩的評論,有驚喜。

UDP只管發送,其他啥都不管。所以UDP「通常意義」上會比TCP快,尤其在穩定的網路裡頭。但代價就是,要確保數據完整性準確性,就只能通過更多代碼和邏輯去處理了。一定程度上說,通過UDP實現的協議,是在一個可接受的範圍內,實現了一套縮減版的TCP,犧牲數據完整性檢查來換取速度。廣泛用於穩定的內網傳輸以及允許丟失數據的實時通信。

描述比較泛,有錯請指正。謝謝。


對於UDP,它不能保證數據送達對端協議棧
1.對端協議棧沒收到數據,即使應用層調用recvfrom(),也收不到數據。
2.對端協議棧收到數據,應用層可能還沒調用recvfrom(),也相當於應用層沒收到數據。
不管UDP有沒有把送據送到對端協議棧,都是只有對端應用層知道自己有沒有收到數據,UDP是不知道。
對於TCP,它保證數據送達對端協議棧
1.對端應用層調用了recv(),應用層知道自己收到了數據,但是TCP並不知道。TCP只知道數據已經送達了協議棧,並不知道應用層做了什麼。
2.對端應用層忙著處理其他事情(比如處理定時器或其他的網路I/O),還沒來得及調用recv(),TCP也是不知道的。

所以TCP和UDP都是不知道對端應用層有沒有收到數據,如果要讓發送端應用層知道,就必須對端應用層發送一個ACK。

讓TCP來確認應用層有沒有收到數據,這本身就是不合理的,不只是效率問題,TCP提供了自適應的超時及重傳,流量控制等等策略來保證數據高效,穩定地送達對端協議棧,已經完成了傳輸層的使命,應用層的事情,應該由應用層自己來做。


用TCP的話就記住一點,TCP是可以保證有序的,但是不能保證斷線後立刻發送失敗。打個比方,我發了1-10總共10個包,如果在發送第5個的時候連接斷了,後面6-10也是一定發送失敗的,但是操作系統發送數據是有緩存和非同步的,所以沒準在發到第8個包的時候才拋出異常。


提個在實際應用中出現的問題,我需要做wifi遠程控制,我先採用了應答的方式,可是在弱信號測試中發現,控制成功卻回復超時的問題,為了確保可靠,就使用了三次應答(一問一答再確認答收到)的方式,可是卻出現了『控制成功』 『回復也成功』 『確認回復收到』不成功問題。雖然我知道弱網路下的通信本來就不可靠,但是沒有辦法,產品必須要做好。所以現在很頭疼啊。各位大神能不能提供個可靠網路通信的思路或者資料。那些阿里巴巴的程序員們是怎樣做的,金錢交易不是要更可靠嗎?


我覺得沒必要考慮這麼多,如果你還考慮從傳輸層到應用層的可靠性,那真是沒完沒了了。用UDP倒是需要。認為如果出現死鎖或者阻塞問題只能說明程序有bug,即使出現了擁塞控制,TCP也會幫你解決接收端過慢的問題。


我沒有去如此深入的研究多OS,網路通信,所以我作為技術的半吊子對這幫牛人的討論做個評價。
1、我覺得這麼激烈的討論非常好,評論技術,學術上的爭論不能說心胸狹隘。銷售人員可以出去滿嘴跑火車,可以掩蓋產品的瑕疵,但是作為搞技術的人來不得半點馬虎。
2、我自己用libevent和netty, 但是沒用過muduo,看到陳碩本人的測試寫的很漂亮,都搞贏了兩個知名的庫,我就百度一下了解到底什麼玩意。所以才有幸到這裡來。
3、我自己沒仔細看過多少源碼,所以水手說的void send沒資格討論。但是我覺得tcp可靠性的理解上,水手說的很有理。tcp本來設計上就是完全可靠的傳輸方式,其他爭論的各位是把tcp傳輸過程中的異常作為tcp不可靠的理由。網路故障,或者系統故障是可以導致tcp沒法把數據準確傳輸完成,但是tcp可以告訴你連接出錯了,這就是可靠。在上層上做ACK的話那是基於應用層的需求,而不是傳過來的數據是不是跟發送方的不一樣。
4、感覺水手確實是個高手,就是改不了技術人員沒耐心的壞脾氣。
5、結論是,還是繼續用libevent和netty吧。


推薦閱讀:

上萬元的人體工學椅到底好在哪?
為什麼編程0基礎隨便出來培訓4個月就能拿到不錯的工資,哪我們上大學學計科專業幹啥?
IPv6 地址什麼時候會耗盡?
有沒有一種設備可以在沒網路的環境下異地監測的?
b站全屏輸入彈幕這個功能很難做嗎?

TAG:程序員 | 編程 | 計算機網路 | 網路編程 | TCP |