如果沒有 listen,能否建立 TCP 連接?
在 TCP 網(wǎng)絡(luò)通信過程中,我們都是先有 server 端調(diào)用 listen 監(jiān)聽某個(gè)端口號(hào),然后 client 向 server 發(fā)起連接請(qǐng)求,最終建立起連接。
那么,要是沒有一端進(jìn)行監(jiān)聽,是否可以建立起 TCP 連接呢?
對(duì)于 TCP 來(lái)講,一條鏈路的建立是通過三次握手來(lái)完成,而三次握手的過程是由內(nèi)核完成的,顯然三次握手應(yīng)用程序是無(wú)法干擾的。
因此我們可能會(huì)想到使用原始套接字來(lái)接收 IP 報(bào)文,通過應(yīng)用層來(lái)構(gòu)造三次握手報(bào)文來(lái)完成三次握手的過程,但是該方法是行不通的,因?yàn)樵继捉幼衷谑盏綄?duì)端的回應(yīng)報(bào)文 syn+ack 報(bào)文時(shí),系統(tǒng)會(huì)自動(dòng)給對(duì)端回應(yīng) RST 報(bào)文中斷連接。 該現(xiàn)象原來(lái)的文章分析過,本文不在分析。若要實(shí)現(xiàn)套接字的三次握手成功,需要解決系統(tǒng)自動(dòng)回應(yīng) RST 報(bào)文,比如通過 iptable 過濾掉 RST 報(bào)文。
在TCP的三次握手中,client 收到對(duì)端回應(yīng)的 syn+ack 報(bào)文后,之所以能找到對(duì)應(yīng)的套接口,是因?yàn)樵?connect 時(shí)根據(jù)端口號(hào)把套接字加入到 tcp_hashinfo.ehash 的 hash 表中。而原始套接字會(huì)自動(dòng)回復(fù) RST 報(bào)文,就是沒有在 hash 表中加入套接字,導(dǎo)致找不到套接字。
因此我們可以得到,只要避免找不到套接字就可以完成鏈路的建立。所以,兩端同時(shí)打開套接字也可完成鏈路的建立,并不需要其中一端進(jìn)行 listen。
同時(shí)連接
測(cè)試步驟就是2臺(tái)機(jī)器各自綁定一個(gè)本地地址和端口號(hào),然后同時(shí)向?qū)Χ私壎ǖ亩丝诎l(fā)送connect 請(qǐng)求,具體例子不再貼出。
在同時(shí)連接中,兩端同時(shí)發(fā)送 SYN 報(bào)文而進(jìn)入 SYN_SENT 狀態(tài);當(dāng)每一端收到 SYN 后狀態(tài)變?yōu)?SYN_RCVD, 發(fā)送 SYN 并對(duì)收到的 SYN 進(jìn)行確認(rèn);當(dāng)雙方都收到對(duì)端的 SYN 及相應(yīng)的 ACK, 狀態(tài)變遷為 ESTABLISHED。狀態(tài)變遷過程如下:
以上是兩端互相 connect 完成鏈路的建立,若要去掉兩端,我們也可以實(shí)現(xiàn)connect 本端綁定的IP和端口號(hào)。
使用 nc 測(cè)試一個(gè) 自己連接自己的 TCP 連接
上述可以看到源端口號(hào)等于目的端口號(hào),并且也完成鏈路的建立。
從跟蹤結(jié)果來(lái)看,自己連接自己(公用同一個(gè)socket)完成了鏈路的建立。
自己連接自己只是同時(shí)連接中的一個(gè)特例。