tcp 編程中,connect 連接成功的標準是什麼?

tcp 編程時,在雙方正式通信之前,首先需要經過三次握手。

但是據我所知,三次握手中,第三次發送ACK的時候,如果此時ACK丟失,那麼服務端在一段時間內沒收到 ACK,服務端就會重新發送 SYN+ACK,以確保正確建立連接。

而客戶端在發出ACK的時候,狀態就由 SYN_SEND 轉換為了 ESTABLISHED ,如果在ACK丟失的情況下(此時並沒有真正建立連接,但客戶端以為已經建立連接了),客戶端開始send數據給服務端,將會觸發 RST,關閉連接。

現在,我想要問的是,在編程的時候,我們通常使用 connect 用於連接服務端,完成三次握手。但是 connect 連接成功的標準到底是什麼?
1)是客戶端發送出去了 ACK 就算成功?
2)還是保證 ACK 被服務端收到了? 那又是怎麼保證的? 是在一定時間內,不會再次收到來自服務端的 ACK+SYN ,就算真正連接成功了?

由此,可以再引出一個問題,服務端 accept 成功的標準是什麼?
1)是正確收到來自客戶端的 ACK ?

------------------------------------------------------華麗的分割線---------------------------------------------

附上我的理解:
BSD 在最初設計 socket 的時候,應該考慮過這個問題,個人猜測應該是 connect 成功了 accept 就一定成功了,這兩者應該配套的。 也就是說 connect 連接成功的標準應該是確定了 ACK 被服務端收到。
我的理解純屬個人YY,歡迎各位大牛答疑解惑,或者能夠給出一種驗證的方案。
拜謝!!!


客戶端發完第三次ACK,就立馬為established狀態。即使第三次ACK丟失,客戶端並不關心,可以發送數據,格式為ACK + Data,當到達伺服器端,伺服器狀態雖然為SYN_Receive,它依然可以將Data緩存下來,客戶端捎帶過來的ACK,這個ACK就是對伺服器SYN+ACK又一次確認,所以伺服器端立馬切換為established 狀態,然後可以將緩存下來的客戶端Data提交給應用程序。


所以客戶端只要發送了三次握手的ACK即認為自己為established 狀態,對應的就是 connect 成功返回,同時可以接受用戶數據。


而伺服器端如果沒有接收到客戶端ACK,accept依然為阻塞狀態,直到由於客戶端發送數據+ ACK或自己超時重傳SYN+ACK並收到客戶端ACK,accept才會成功返回。

我的觀點得到了RFC793的佐證,以下為RFC793 31頁的摘選:

3.4. Establishing a connection
Several examples of connection initiation follow. Although these examples do not show connection synchronization using data-carrying segments, this is perfectly legitimate, so long as the receiving TCP doesn』t deliver the data to the user until it is clear the data is valid (i.e., the data must be buffered at the receiver until the connection reaches the ESTABLISHED state). The three-way handshake reduces the possibility of false connections. It is the implementation of a trade-off between memory and messages to provide information for this checking.


更多關於TCP連接的詳細闡述,請參考:
http://www.zhihu.com/question/24853633/answer/115173386


1,只要client收到server的syn+ack,發送ack,client就認為三次握手建立完成,狀態變為Established。阻塞的connect調用成功返回;對於非阻塞的connect調用,都會立馬返回,使用select、poll或者epoll機制來判斷socket是否可寫來判定連接是否建立。
Established狀態下的client自然是可以發送數據給server的。

2,如果client的最後一個ack,中途丟失或者被server主動忽略或丟棄,server在定時器溢出後,會重發syn+ack,client向server發送數據,server並不會返回Rst。
要模擬這種場景,有兩種方式:1)收到server的syn+ack後,拔掉客戶端的網線;2)讓server端的accept隊列塞滿,主動丟棄ack。第一種場景模擬起來,難度比較大,於是我們採用第二種。
我採用go寫的server來模擬。將net.core.somaxconn設置為2(因為go中listen調用,傳入的即為系統的上限制),方便模擬。

server.go
1 package main
2
3 import (
4 "log"
5 "net"
6 )
7
8 func main() {
9 var c chan struct{}
10 _, err := net.Listen("tcp", ":8081")
11 if err != nil {
12 log.Fatal(err)
13 }
14
15 &<-c 16 }

客戶端發送10個connect請求。

測試結果中,反映了下面幾點:
1)客戶端在發送完ack後(13:48:52.999239),立馬(13:48:52.999879)開始發送數據;
2)發送的數據超時後,開始重試;
3)收到伺服器重發的syn+ack後,客戶端發送ack;
4)當伺服器的syn+ack重試次數超過系統設置值後,斷開連接,發送Rst包給客戶端。

結論:客戶端發送完最後一個ack後,就認為連接建立,connect成功!

3,accept成功?這個更簡單了,accept函數調用只是去accept隊列中取出一個連接而已,對伺服器而言,只要接收了客戶端的ack(注意,不忽略不拋棄哦),狀態變更為Established了,而不是accept調用成功才變成Established的。

4,tcp的backlog隊列
linux在實現tcp協議棧的時候,採用了兩個隊列,syn和accept隊列。
當接收了對端的syn後,將其放入syn隊列;當接收了對端的ack後,將syn隊列中的連接轉移到accept隊列。

5,如果backlog隊列滿了,咋辦?如果伺服器端Established了,但突然客戶端斷網了,伺服器進入半連接,咋辦?如果客戶端不停的發送syn包咋辦?等等。。。很多問題。。這將會在我的淺析TCP(上) - 曹東的文章 - 知乎專欄 中篇中詳細介紹,敬請期待!


推薦閱讀:

tcp連接的問題?
前途未卜的准程序員,吃過苦頭有智慧的或者過來人前輩大牛有什麼樣的忠告給當局者?
金融工程的新生怎麼選購筆記本電腦?
浙大的計算機和電氣從就業來看選哪個好?
知乎上最牛的程序員有辦法知道任意匿名用戶是誰嗎?

TAG:編程 | 計算機網路 | TCPIP | TCP | 網路協議 |