一條 TCP 連線的建立傳統上少不了的就是三相交握的過程,執行三項交握有許多優點,包括增加連線的可靠性、同時也允許連線的雙方協調連線的設定,例如是否使用 Window-scaling 等等,也能避免一些常見的攻擊模式,像是 IP Spoofing 或是被用來當作反射攻擊、放大攻擊的節點。但是三項交握同時也伴隨 overhead,這些成本在區域網路中因為延遲低 (小於 0.1ms) 並不明顯,但如果將場景搬到跨洲的網路時,這些成本就變得無法忽視。
舉個例子,當你在利用 FTP 把 10000 個檔案從美國傳回台灣,因為 FTP 的特性會為每一個檔案建立一條新的連線。考慮美國到台灣的 RTT 大概是 200ms,每建立一個連線就要花掉 2*RTT 的時間,等於是 0.4 秒鐘,而這只是一個檔案。10000 個檔案光連線建立就要花費 4000 秒,一個多小時就這樣過去了,還不包括實際傳輸檔案的時間。要解決這樣的問題,一般來說就是盡量使用 persistent connection 或是口語一點的 long-lived connection,避免建立新的連線、重複使用舊的連線,事實上這種作法還有一個好處就是避免 TCP Slow-start,不用每條連線都等待 congestion window ramp-up。然而像 FTP 這種早就該放進博物館卻還是有一堆人在使用。
另一個例子則是之前我有特別寫文章介紹的 DNS-over-HTTPS,用 TCP 來做 DNS Server 就是典型的 short-lived connection 的例子,想像你要解個 domain 要至少花上 2*RTT 的時間 (當然這邊還有其他議題,例如 TLS Handshake 本身就要花上 2*RTT,當然有些解法,像是 TLS False-start 及 TLS 1.3 的 Session resumption,等哪天有空再補上)。為此 RFC 7413 就在 TCP 中延伸了一個 Fastopen 的方法來加快連線建立的速度。
TFO 是在三項交握的過程 (沒錯還是有三項交握,但只需要一次),在 SYN+ACK 封包裡再加入一個 TFO Cookie,對於第一次的連線沒有太多的影響,但是後續的連線就可以直接在 SYN 封包中帶入 Cookie 與 data,大大的降低連線的時間 (從 2*RTT 到近乎 0),按上面提到的例子就是 10000 個檔案會提早一個多小時傳完。
至於要怎麼樣開啟 TCP Fastopen 來享受它的好處呢?在 Linux 3.7 版後支援了 TFO,可以調整 net.ipv4.tcp_fastopen
: 1
sysctl -w net.ipv4.tcp_fastopen=3
FreeBSD 的話目前來說還是 experimental 的階段,必須要在 kernel 設定中加入 options TCP_RFC7413
來加入這個功能,不過聽說在 11.2-R 好像會直接放入 GENERIC 裡面。而確切要啟動這個功能則可以透過調整 net.inet.tcp.fastopen.client_enable
及 net.inet.tcp.fastopen.server_enable
: 1
2sysctl -w net.inet.tcp.fastopen.client_enable=1 # 客戶端使用 TFO
sysctl -w net.inet.tcp.fastopen.server_enable=1 # 伺服器端使用 TFO
那最後一個問題是,原則上 TCP Fastopen 要 Client 跟 Server 都支援才有用嘛,那到底很多人用的 Windows 有沒有支援呢?