在 TCP 網(wǎng)絡(luò)通信過程中,我們都是先有 server 端調(diào)用 listen 監(jiān)聽某個端口號,然后 client 向 server 發(fā)起連接請求,最終建立起連接。
那么,要是沒有一端進行監(jiān)聽,是否可以建立起 TCP 連接呢?
對于 TCP 來講,一條鏈路的建立是通過三次握手來完成,而三次握手的過程是由內(nèi)核完成的,顯然三次握手應(yīng)用程序是無法干擾的。
因此我們可能會想到使用原始套接字來接收 IP 報文,通過應(yīng)用層來構(gòu)造三次握手報文來完成三次握手的過程,但是該方法是行不通的,因為原始套接字在收到對端的回應(yīng)報文 syn+ack 報文時,系統(tǒng)會自動給對端回應(yīng) RST 報文中斷連接。 該現(xiàn)象原來的文章分析過,本文不在分析。若要實現(xiàn)套接字的三次握手成功,需要解決系統(tǒng)自動回應(yīng) RST 報文,比如通過 iptable 過濾掉 RST 報文。
在TCP的三次握手中,client 收到對端回應(yīng)的 syn+ack 報文后,之所以能找到對應(yīng)的套接口,是因為在 connect 時根據(jù)端口號把套接字加入到 tcp_hashinfo.ehash 的 hash 表中。而原始套接字會自動回復(fù) RST 報文,就是沒有在 hash 表中加入套接字,導(dǎo)致找不到套接字。
因此我們可以得到,只要避免找不到套接字就可以完成鏈路的建立。所以,兩端同時打開套接字也可完成鏈路的建立,并不需要其中一端進行 listen。
同時連接
測試步驟就是2臺機器各自綁定一個本地地址和端口號,然后同時向?qū)Χ私壎ǖ亩丝诎l(fā)送connect 請求,具體例子不再貼出。
在同時連接中,兩端同時發(fā)送 SYN 報文而進入 SYN_SENT 狀態(tài);當(dāng)每一端收到 SYN 后狀態(tài)變?yōu)?SYN_RCVD, 發(fā)送 SYN 并對收到的 SYN 進行確認(rèn);當(dāng)雙方都收到對端的 SYN 及相應(yīng)的 ACK, 狀態(tài)變遷為 ESTABLISHED。狀態(tài)變遷過程如下:
以上是兩端互相 connect 完成鏈路的建立,若要去掉兩端,我們也可以實現(xiàn)connect 本端綁定的IP和端口號。
使用 nc 測試一個 自己連接自己的 TCP 連接
# nc 10.115.20.30 1234 -p 1234
# netstat -anp | grep 1234
tcp 0 0 10.115.20.30:1234 10.115.20.30:1234 ESTABLISHED 2050/nc
上述可以看到源端口號等于目的端口號,并且也完成鏈路的建立。
# strace nc 10.115.20.30 1234 -p 1234
execve("/usr/bin/nc", ["nc", "10.115.20.30", "1234", "-p", " 1234"], [/* 31 vars */]) = 0
brk(NULL) = 0x23d4000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f213f394000
...
munmap(0x7f213f393000, 4096) = 0
open("/usr/share/ncat/ca-bundle.crt", O_RDONLY) = -1 ENOENT (No such file or directory)
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
//bind后直接就是connect,并沒有進行listen
connect(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("10.115.20.30")}, 16) = -1 EINPROGRESS (Operation now in progress)
select(4, [3], [3], [3], {10, 0}) = 1 (out [3], left {9, 999998})
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
select(4, [0 3], [], [], NULL
從跟蹤結(jié)果來看,自己連接自己(公用同一個socket)完成了鏈路的建立。
自己連接自己只是同時連接中的一個特例。
審核編輯:劉清
-
SYN
+關(guān)注
關(guān)注
0文章
9瀏覽量
8204 -
RST
+關(guān)注
關(guān)注
0文章
31瀏覽量
7403 -
TCP通信
+關(guān)注
關(guān)注
0文章
146瀏覽量
4243
發(fā)布評論請先 登錄
相關(guān)推薦
評論