<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          連接一個 IP 不存在的主機(jī)時,握手過程是怎樣的?

          共 10231字,需瀏覽 21分鐘

           ·

          2021-07-17 13:57

          文章持續(xù)更新,可以微信搜一搜「小白debug」第一時間閱讀,回復(fù)【教程】獲golang免費(fèi)視頻教程。本文已經(jīng)收錄在GitHub https://github.com/xiaobaiTech/golangFamily , 有大廠面試完整考點(diǎn)和成長路線,歡迎Star。

          鴿了好長時間了,最近很忙。以前工作忙完,就抽空寫文章。

          現(xiàn)在忙完工作,還要一三五學(xué)駕照,二四六看家具。有同感的老鐵們不要舉手,拉到右下角點(diǎn)個"在看"就好了。

          真的,全怪某音。

          扯遠(yuǎn)了,回到今天的主題。

          方兄最近寫了篇很贊的文章 寫給想去字節(jié)寫 Go 的你 ,里面提到了兩個問題。

          連接一個 IP 不存在的主機(jī)時,握手過程是怎樣的?

          連接一個 IP 地址存在但端口號不存在的主機(jī)時,握手過程又是怎樣的呢?

          讓我回想起曾經(jīng)也被面試官問過類似的問題,意識到應(yīng)該很多朋友會對這個問題感興趣。

          所以來給大家嘮嘮。

          這兩個問題可以延伸出非常多的點(diǎn)。

          看完了,說不定能加分!


          正常情況的握手過程是怎么樣的

          上面提到的問題,其實(shí)是指TCP的三次握手流程。這絕對是面試八股文里的老股了。

          我們簡單回顧下基礎(chǔ)知識點(diǎn)。

          正常情況下的TCP三次握手

          服務(wù)端啟動好后會調(diào)用 listen() 方法,進(jìn)入到 LISTEN 狀態(tài),然后靜靜等待客戶端的連接請求到來。

          而此時客戶端主動調(diào)用 connect(IP地址) ,就會向某個IP地址發(fā)起第一次握手,發(fā)送SYN 到目的服務(wù)器。

          服務(wù)器在收到第一次握手后就會響應(yīng)客戶端,這是第二次握手

          客戶端在收到第二次握手的消息后,響應(yīng)服務(wù)的一個ACK,這算第三次握手,此時客戶端 就會進(jìn)入 ESTABLISHED狀態(tài),認(rèn)為連接已經(jīng)建立完成。

          通過抓包可以直觀看出三次握手的流程。

          正常三次握手抓包


          連一個 IP 不存在的主機(jī)時,握手過程是怎樣的

          那不存在的IP,分兩種,局域網(wǎng)內(nèi)和局域網(wǎng)外的。

          家用路由器局域網(wǎng)互聯(lián)

          我以我家里的情況舉例。

          家里有一臺家用路由器。本質(zhì)上它的功能已經(jīng)集成了我們常說的路由器,交換機(jī)和無線接入點(diǎn)的功能了。

          其中路由器和交換機(jī)在之前寫過的 《硬核圖解!30張圖帶你搞懂!路由器,集線器,交換機(jī),網(wǎng)橋,光貓有啥區(qū)別?》里已經(jīng)詳細(xì)介紹過了,就不再說一遍了。無線接入點(diǎn)基本可以認(rèn)為就是個放出 wifi 信號的組件。

          家用路由器下,連著我的N臺設(shè)備,包括手機(jī)和電腦,他們的IP都有個共同點(diǎn)。都是 192.168.31.xx  形式的。其中,我的電腦的IP是192.168.31.6 ,這個可以通過 ifconfig查到。

          符合這個形式的這些個設(shè)備,本質(zhì)上就是通過各種設(shè)備(wifi或交換機(jī)等)接入到上圖路由器的e2端口,他們共同構(gòu)成一個局域網(wǎng)

          因此,在我家,我們可以粗暴點(diǎn)認(rèn)為只要是  192.168.31.xx  形式的IP,就是局域網(wǎng)內(nèi)的IP。否則就是局域網(wǎng)外的IP,比如 192.0.2.2


          目的IP在局域網(wǎng)內(nèi)

          因?yàn)橥ㄟ^ ifconfig 可以查到我的局域網(wǎng)內(nèi)IP是192.168.31.6 ,這里盲猜末尾+1是不存在的 IP 。試了下,192.168.31.7 還真不存在。

          $ ping 192.168.31.7
          PING 192.168.31.7 (192.168.31.7): 56 data bytes
          Request timeout for icmp_seq 0
          Request timeout for icmp_seq 1
          Request timeout for icmp_seq 2
          Request timeout for icmp_seq 3
          ^C
          --- 192.168.31.7 ping statistics ---
          5 packets transmitted, 0 packets received, 100.0% packet loss

          于是寫個程序嘗試連這個IP 。下面的代碼是 golang 寫的,大家不看代碼也沒關(guān)系,放出來只是方便大家自己復(fù)現(xiàn)的時候用的。

          // tcp客戶端
          package main

          import (
              "fmt"
              "io"
              "net"
              "os"
          )

          func main() {
              client, err := net.Dial("tcp""192.168.31.7:8081")
              if err != nil {
                  fmt.Println("err:", err)
                  return
              }

              defer client.Close()
              go func() {
                  input := make([]byte1024)
                  for {
                      n, err := os.Stdin.Read(input)
                      if err != nil {
                          fmt.Println("input err:", err)
                          continue
                      }
                      client.Write([]byte(input[:n]))
                  }
              }()

              buf := make([]byte1024)
              for {
                  n, err := client.Read(buf)
                  if err != nil {
                      if err == io.EOF {
                          return
                      }
                      fmt.Println("read err:", err)
                      continue
                  }
                  fmt.Println(string(buf[:n]))
              }
          }

          然后嘗試抓包。

          連一個不存在的IP(局域網(wǎng)內(nèi))抓包

          可以發(fā)現(xiàn)根本沒有三次握手的包,只有一些 ARP 包,在詢問“誰是 192.168.31.7,告訴一下 192.168.31.6” 。

          這里有三個問題

          • 為什么會發(fā)ARP請求?

          • 為什么沒有TCP握手包?

          • ARP本身是沒有重試機(jī)制的,為什么ARP請求會發(fā)那么多遍?

          首先我們看下正常情況下執(zhí)行connect,也就是第一次握手 的流程。

          正常connect的流程

          應(yīng)用層執(zhí)行connect過后,會通過socket層,操作系統(tǒng)接口,進(jìn)程會從用戶態(tài)進(jìn)入到內(nèi)核態(tài),此時進(jìn)入 傳輸層,因?yàn)槭?strong style="font-size: inherit;line-height: inherit;color: rgb(41, 98, 255);">TCP第一次握手,會加入TCP頭,且置SYN標(biāo)志。

          tcp報頭的SYN

          然后進(jìn)入網(wǎng)絡(luò)層,我想要連的是 192.168.31.7 ,雖然它是我瞎編的,但IP頭還是得老老實(shí)實(shí)把它加進(jìn)去。

          此時需要重點(diǎn)介紹的是鄰居子系統(tǒng),它在網(wǎng)絡(luò)層和數(shù)據(jù)鏈路層之間。可以通過ARP協(xié)議將目的IP轉(zhuǎn)為對應(yīng)的MAC地址,然后數(shù)據(jù)鏈路層就可以用這個MAC地址組裝幀頭

          我們看下那么ARP協(xié)議的流程

          ARP流程

          1.先到本地ARP表查一下有沒有 192.168.31.7 對應(yīng)的 mac地址,有的話就返回,這里顯然是不可能會有的。

          可以通過 arp -a 命令查看本機(jī)的 arp表都記錄了哪些信息

          $ arp -a
          ? (192.168.31.1) at 88:c1:97:59:d1:c3 on en0 ifscope [ethernet]
          ? (224.0.0.251) at 1:0:4e:0:1:fb on en0 ifscope permanent [ethernet]
          ? (239.255.255.250) at 1:0:3e:7f:ff:fb on en0 ifscope permanent [ethernet]

          2.看下 192.168.31.7  跟本機(jī)IP  192.168.31.6在不在一個局域網(wǎng)下。如果在的話,就在局域網(wǎng)內(nèi)發(fā)一個 arp 廣播,內(nèi)容就是 前面提到的 “誰是 192.168.31.7,告訴一下 192.168.31.6”。

          3.如果目的IP跟本機(jī)IP不在同一個局域網(wǎng)下,那么會去獲取默認(rèn)網(wǎng)關(guān)的MAC地址,這里就是指獲取家用路由器的MAC地址。然后把消息發(fā)給家用路由器,讓路由器發(fā)到互聯(lián)網(wǎng),找到下一跳路由器,一跳一跳的發(fā)送數(shù)據(jù),直到把消息發(fā)到目的IP上,又或者找不到目的地最終被丟棄。

          4.第2和第3點(diǎn)都是本地沒有查到 ARP 緩存記錄的情況,這時候會把SYN報文放進(jìn)一個隊(duì)列(叫unresolved_queue)里暫存起來,然后發(fā)起ARP請求;等ARP層收到ARP回應(yīng)報文之后,會再從緩存中取出 SYN 報文,組裝 MAC 幀頭,完成剛剛沒完成的發(fā)送流程。

          如果經(jīng)過 ARP 流程能正常返回 MAC 地址,那皆大歡喜,直接給數(shù)據(jù)鏈路層,經(jīng)過 ring buffer 后傳到網(wǎng)卡,發(fā)出去。

          但因?yàn)楝F(xiàn)在這個IP是瞎編的,因此不可能得到目的地址 MAC ,所以消息也一直沒法到數(shù)據(jù)鏈路層。整個流程卡在了ARP流程中。

          抓包是在數(shù)據(jù)鏈路層之后進(jìn)行的,因此 TCP 第一次握手的包一直沒能抓到,只能抓到為了獲得  192.168.31.7 的MAC地址的ARP請求。

          發(fā)送數(shù)據(jù)時,是在經(jīng)過數(shù)據(jù)鏈路層之后的  dev_queue_xmit_nit 方法執(zhí)行抓包操作的,這是屬于網(wǎng)卡驅(qū)動層的方法了。

          順帶一提,接收端抓包是在  __netif_receive_skb_core 方法里執(zhí)行的,也屬于網(wǎng)卡驅(qū)動層。感興趣的朋友們可以以這個為關(guān)鍵詞搜索相關(guān)知識點(diǎn)哈

          此時 因?yàn)?TCP 協(xié)議是可靠的協(xié)議,對于 TCP 層來說,第一次握手的消息,已經(jīng)發(fā)出去了,但是一直沒有收到 ACK。也不知道消息是出去后是遇到什么事了。為了保證可靠性,它會不斷重發(fā)。

          而每一次重發(fā),都會因?yàn)橥瑯拥脑颍]有目的 MAC 地址)而尬在了 ARP 那個流程里。因此,才看到好幾次重復(fù)的 ARP 消息。

          那回到剛剛的三個問題

          • 為什么會發(fā) ARP 請求?

            因?yàn)槟康牡刂肥窍咕幍模镜谹RP表沒有目的機(jī)器的MAC地址,因此發(fā)出ARP消息。

          • 為什么沒有 TCP 握手包?

            因?yàn)閰f(xié)議棧的數(shù)據(jù)到了網(wǎng)絡(luò)層后,在數(shù)據(jù)鏈路層前,就因?yàn)闆]有目的MAC地址,沒法發(fā)出。因此抓包軟件抓不到相關(guān)數(shù)據(jù)。

          • 為什么 ARP 請求會發(fā)那么多遍?

            因?yàn)?TCP 協(xié)議的可靠性,會重發(fā)第一次握手的消息,但每一次都因?yàn)闆]有目的 MAC 地址而失敗,每次都會發(fā)出ARP請求。


          小結(jié)

          連一個 IP 不存在的主機(jī)時,如果目的IP在局域網(wǎng)內(nèi),則第一次握手會失敗,接著不斷嘗試重發(fā)握手的請求。同時,本機(jī)會不斷發(fā)出ARP請求,企圖獲得目的機(jī)器的 MAC 地址。并且,因?yàn)闆]能獲得目的 MAC 地址,這些 TCP 握手請求最終都發(fā)不出去,


          目的IP在局域網(wǎng)外

          上面提到的是,目的 IP 在局域網(wǎng)內(nèi)的情況,下面討論目的IP在局域網(wǎng)外的情況。

          瞎編一個不是  192.168.31.xx 形式的 IP 作為這次要用的局域網(wǎng)外IP, 比如 10.225.31.11

          先抓包看一下。

          連一個不存在的IP(局域網(wǎng)外)抓包

          這次的現(xiàn)象是能發(fā)出 TCP 第一次握手的 SYN包

          這里有兩個問題

          • 為什么連局域網(wǎng)外的 IP 現(xiàn)象跟連局域網(wǎng)內(nèi)不一致?

          • TCP 第一次握手的重試規(guī)律好像不太對?


          為什么連局域網(wǎng)外的IP現(xiàn)象跟連局域網(wǎng)內(nèi)不一致?

          這個問題的答案其實(shí)在上面 ARP 的流程里已經(jīng)提到過了,如果目的 IP 跟本機(jī) IP 不在同一個局域網(wǎng)下,那么會去獲取默認(rèn)網(wǎng)關(guān)的 MAC 地址,這里就是指獲取家用路由器的MAC地址

          此時ARP流程成功返回家用路由器的 MAC 地址,數(shù)據(jù)鏈路層加入幀頭,消息通過網(wǎng)卡發(fā)到了家用路由器上

          消息會通過互聯(lián)網(wǎng)一直傳遞到某個局域網(wǎng)為  10.225.31.xx 的路由器上,那個路由器 發(fā)出ARP 請求,詢問他們局域網(wǎng)內(nèi)的機(jī)器有沒有叫 10.225.31.11的 (結(jié)果當(dāng)然沒有)。

          最終沒能發(fā)送成功,發(fā)送端也就遲遲收不到目的機(jī)的第二次握手響應(yīng)。

          因此觸發(fā)TCP重傳。


          TCP第一次握手的重試規(guī)律好像不太對?

          在 Linux 中,第一次握手的 SYN 重傳次數(shù),是通過 tcp_syn_retries 參數(shù)控制的。可以通過下面的方式查看

          $cat /proc/sys/net/ipv4/tcp_syn_retries
          6

          這里的含義是指 syn重傳 會發(fā)生6次。

          而每次重試都會間隔一定的時間,這里的間隔一般是 1s,2s,4s,8s, 16s, 32s .

          SYN重傳

          而事實(shí)上,看我的截圖,是先重試4次,每次都是1s,之后才是 1s,2s,4s,8s, 16s, 32s 的重試。

          這跟我們知道的不太一樣。

          這個是因?yàn)?strong style="font-size: inherit;line-height: inherit;color: rgb(41, 98, 255);">我用的是macOS抓的包,跟linux就不是一個系統(tǒng),各自的TCP協(xié)議棧在sync重傳方面的實(shí)現(xiàn)都可能會有一定的差異。

          我還聽說 oppovivo 的 syn重傳 是0.5s起步的。而 windows 的 syn重傳 還有自己的專利。

          這些冷知識大家可以不用在意。面試的時候知道linux的就夠了,剩下的可以用來裝逼。畢竟面試官不在意"茴"字到底有幾種寫法。


          連IP 地址存在但端口號不存在的主機(jī)的握手過程

          前面提到的是IP地址壓根就不存在的情況。假如IP地址存在但端口號是瞎編的呢?

          目的IP是回環(huán)地址

          連回環(huán)地址,端口不存在抓包

          現(xiàn)象也比較簡單,已經(jīng)IP地址是存在的,也就是在互聯(lián)網(wǎng)中這個機(jī)器是存在的。

          那么我們可以正常發(fā)消息到目的IP,因?yàn)閷?yīng)的MAC地址和IP都是正確的,所以,數(shù)據(jù)從數(shù)據(jù)鏈路層到網(wǎng)絡(luò)層都很OK。

          直到傳輸層,TCP協(xié)議在識別到這個端口號對應(yīng)的進(jìn)程根本不存在時,就會把數(shù)據(jù)丟棄,響應(yīng)一個RST消息給發(fā)送端。

          連回環(huán)地址時端口不存在


          RST是什么?

          我們都是到TCP正常情況下斷開連接是用四次揮手,那是正常時候的優(yōu)雅做法。

          異常情況下,收發(fā)雙方都不一定正常,連揮手這件事本身都可能做不到,所以就需要一個機(jī)制去強(qiáng)行關(guān)閉連接。

          RST 就是用于這種情況,一般用來異常地關(guān)閉一個連接。它在TCP包頭中,在收到置了這個標(biāo)志位的數(shù)據(jù)包后,連接就會被關(guān)閉,此時接收到 RST的一方,一般會看到一個 connection reset 或  connection refused 的報錯。

          TCP報頭RST位


          目的IP在局域網(wǎng)內(nèi)

          剛剛提到我的本機(jī)IP是 192.168.31.6 ,局域網(wǎng)內(nèi)有臺 192.168.31.1 。同樣嘗試連一個不存在的端口。

          連存在的局域網(wǎng)內(nèi)IP,端口不存在抓包

          此時現(xiàn)象跟前者一致。

          唯一不同的是,前者是回環(huán)地址,RST數(shù)據(jù)是從本機(jī)的傳輸層返回的。而這次的情況,RST數(shù)據(jù)是從目的機(jī)器的傳輸層返回的。

          連外網(wǎng)地址時端口不存在


          目的IP在局域網(wǎng)外

          找一個存在的外網(wǎng)ip,這里我拿了最近剛白嫖的阿里云服務(wù)器地址 47.102.221.141 。(炫耀)

          進(jìn)行連接連接,發(fā)現(xiàn)與前面兩種情況是一致的,目的機(jī)器在收到我的請求后,立馬就通過 RST標(biāo)志位 斷開了這次的連接。

          連存在的局域網(wǎng)外IP,端口不存在抓包

          這一點(diǎn)跟前面兩種情況一致。

          熟悉小白的朋友們都知道,每次搞事情做測試,都會用  baidu.com

          這次也不例外,ping 一下 baidu.com ,獲得它的 IP: 220.181.38.148  。

          $ ping baidu.com
          PING baidu.com (220.181.38.148): 56 data bytes
          64 bytes from 220.181.38.148: icmp_seq=0 ttl=48 time=35.728 ms
          64 bytes from 220.181.38.148: icmp_seq=1 ttl=48 time=38.052 ms
          64 bytes from 220.181.38.148: icmp_seq=2 ttl=48 time=37.845 ms
          64 bytes from 220.181.38.148: icmp_seq=3 ttl=48 time=37.210 ms
          64 bytes from 220.181.38.148: icmp_seq=4 ttl=48 time=38.402 ms
          64 bytes from 220.181.38.148: icmp_seq=5 ttl=48 time=37.692 ms
          ^C
          --- baidu.com ping statistics ---
          6 packets transmitted, 6 packets received, 0.0% packet loss
          round-trip min/avg/max/stddev = 35.728/37.488/38.402/0.866 ms

          發(fā)消息到給百度域名背后的 IP,且瞎隨機(jī)指定一個端口 8080, 抓包。

          連baidu,端口不存在抓包

          現(xiàn)象卻不一致。沒有 RST 。而且觸發(fā)了第一次握手的重試消息。這是為什么?

          這是因?yàn)閎aidu的機(jī)器,作為線上生產(chǎn)的機(jī)器,會設(shè)置一系列安全策略,比如只對外暴露某些端口,除此之外的端口,都一律拒絕。

          所以很多發(fā)到 8080端口的消息都在防火墻這一層就被拒絕掉了,根本到不了目的主機(jī)里,而RST是在目的主機(jī)的TCP/IP協(xié)議棧里發(fā)出的,都還沒到這一層,就更不可能發(fā)RST了。因此發(fā)送端發(fā)現(xiàn)消息沒有回應(yīng)(因?yàn)楸环阑饓G了),就會重傳。所以才會出現(xiàn)上述抓包里的現(xiàn)象。

          防火墻安全策略


          總結(jié)

          連一個 IP 不存在的主機(jī)時

          • 如果IP在局域網(wǎng)內(nèi),會發(fā)送N次ARP請求獲得目的主機(jī)的MAC地址,同時不能發(fā)出TCP握手消息。

          • 如果IP在局域網(wǎng)外,會將消息通過路由器發(fā)出,但因?yàn)樽罱K找不到目的地,觸發(fā)TCP重試流程。

          連IP 地址存在但端口號不存在的主機(jī)時

          • 不管目的IP是回環(huán)地址還是局域網(wǎng)內(nèi)外的IP地址,目的主機(jī)的傳輸層都會在收到握手消息后,發(fā)現(xiàn)端口不正確,發(fā)出RST消息斷開連接。

          • 當(dāng)然如果目的機(jī)器設(shè)置了防火墻策略,限制他人將消息發(fā)到不對外暴露的端口,那么這種情況,發(fā)送端就會不斷重試第一次握手。

          最后留個問題,連一個 不存在的局域網(wǎng)外IP的主機(jī)時,我們可以看到TCP的重發(fā)規(guī)律是:開始時,每隔1s重發(fā)五次 TCP SYN消息,接著2s,4s,8s,16s,32s都重發(fā)一次;

          對比連一個 不存在的局域網(wǎng)內(nèi)IP的主機(jī)時,卻是每隔1s重發(fā)了4次ARP請求,接著過了32s后才再發(fā)出一次ARP請求。已知ARP請求是沒有重傳機(jī)制的,它的重試就是TCP重試觸發(fā)的,但兩者規(guī)律不一致,是為什么?


          最后

          歡迎大家加我微信(公眾號里右下角“聯(lián)系我”),互相圍觀朋友圈砍一刀啥的哈哈。

          如果文章對你有幫助,看下文章底部右下角,做點(diǎn)正能量的事情(點(diǎn)兩下)支持一下。(卑微瘋狂暗示,拜托拜托,這對我真的很重要!

          我是小白,我們下期見。

          別說了,一起在知識的海洋里嗆水吧

          關(guān)注公眾號:【小白debug】


          文章推薦:


          瀏覽 38
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久99蜜桃精品 | 翔田千里 50岁 无码 | 人妻喷水 | 伊人网在线视频 | 大香蕉尹人 |