<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>

          4個(gè)實(shí)驗(yàn),徹底搞懂TCP連接的斷開(kāi)

          共 4270字,需瀏覽 9分鐘

           ·

          2021-12-23 14:19

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          看到這個(gè)標(biāo)題你可能會(huì)說(shuō),TCP 連接的建立與斷開(kāi),這個(gè)我熟,不就是三次握手與四次揮手嗎?且慢,腦海中可以先嘗試回答這幾個(gè)問(wèn)題:

          • 四次揮手是誰(shuí)發(fā)起的?
          • 如果斷電/斷網(wǎng)了連接會(huì)斷開(kāi)嗎?
          • 什么情況下沒(méi)有四次揮手連接也會(huì)斷開(kāi)?

          這不是面試,而是遇到了實(shí)際問(wèn)題,至于是什么問(wèn)題,容我先賣(mài)個(gè)關(guān)子,本文也不會(huì)解答,后面會(huì)有一篇專門(mén)的文章來(lái)說(shuō)遇到的問(wèn)題是啥,所以在講實(shí)際問(wèn)題之前,先弄懂理論。

          正常斷開(kāi)

          我們由淺入深,先了解正常情況下 TCP 連接是如何斷開(kāi)的,下圖為 TCP 三次握手與四次揮手的經(jīng)典圖(來(lái)自《TCP/IP詳解卷1》)

          在我們的電腦上,可以使用 python 的SimpleHTTPServer來(lái)快速起一個(gè) http 服務(wù)(http 也是基于 TCP 協(xié)議),比如這樣:

          python -m SimpleHTTPServer 20880

          再通過(guò)nctelnet這兩個(gè)命令來(lái)創(chuàng)建 TCP 連接,比如我測(cè)試使用 nc 來(lái)創(chuàng)建連接

          nc -v ip port

          Connection to ip port [tcp/*] succeeded!表示連接成功

          我們?nèi)绾斡^察這個(gè)連接呢?可以通過(guò)netstatlsof?來(lái)查看這條"連接",這里我使用lsof(mac 與 Linux 系統(tǒng)的 netstat 命令不太一樣,使用起來(lái)有點(diǎn)別扭 )

          lsof -i:20880

          無(wú)論是客戶端還是服務(wù)端都會(huì)占用一個(gè)端口,不過(guò)服務(wù)端端口是固定的,客戶端端口是隨機(jī)的。

          如果我們想看 TCP 連接和斷開(kāi)時(shí)握手揮手的 TCP 報(bào)文怎么查看呢?可以使用tcpdump命令

          三次握手

          tcpdump -A -vv -i any -S host 10.179.245.95

          為了方便查看,和上面的經(jīng)典圖放在了一起

          這里的參數(shù)需要提一下的是-S,如果不加-S參數(shù)看到的第三次握手的ack=1,與書(shū)上的理論不太一樣,其實(shí)這里只是tcpdump簡(jiǎn)化了展示,想看實(shí)際值需要加-S

          這里的Flags [S]/[S.]/[.]

          • [S] 代表 SYN

          • [.] 代表 ACK,[S.] 就是 SYN + ACK

          四次揮手

          命令與抓三次握手相同,我們抓到如下?lián)]手?jǐn)?shù)據(jù)

          • [F] 代表 FIN

          這張圖有點(diǎn)奇怪,四次揮手居然變成了三次,這其實(shí)是 TCP 協(xié)議的實(shí)現(xiàn)問(wèn)題,如果第二次與第三次揮手之間沒(méi)有數(shù)據(jù)發(fā)送,那么被動(dòng)斷開(kāi)連接的一方就可能會(huì)把第二次的 ACK 與 第三次的 FIN 合并為一次揮手。

          當(dāng)然我也抓到過(guò)正常的四次揮手,大概長(zhǎng)這樣

          異常斷開(kāi)

          上面鋪墊了這么多,現(xiàn)在開(kāi)始進(jìn)入正題。

          TCP 連接斷開(kāi)是誰(shuí)發(fā)起的

          我們來(lái)思考一個(gè)問(wèn)題:TCP 連接的斷開(kāi)是誰(shuí)發(fā)起的?程序本身還是操作系統(tǒng)?

          我們來(lái)看一段非常簡(jiǎn)單的 TCP 連接創(chuàng)建與斷開(kāi)的代碼

          tcpAddr,?_?:=?net.ResolveTCPAddr("tcp",?"127.0.0.1:20880")
          conn,?err?:=?net.DialTCP("tcp",?nil,?tcpAddr)
          if?err?!=?nil?{
          ?fmt.Println("Client?connect?error?!?"?+?err.Error())
          ?return
          }

          defer?func()?{
          ?err?:=?conn.Close()
          ?fmt.Println("Client?connect?closed?!")
          ?if?err?!=?nil?{
          ??fmt.Println(err)
          ?}
          }()

          fmt.Println(conn.LocalAddr().String()?+?"?:?Client?connected!")
          time.Sleep(10?*?time.Second)

          運(yùn)行后,效果如下,也符合我們預(yù)期:當(dāng)程序打印Client connected!時(shí),能看到連接,當(dāng)打印Client connect closed!時(shí),連接斷開(kāi)

          如果我們?cè)谶B接斷開(kāi)前使用kill -9強(qiáng)殺進(jìn)程呢?(這里我用了兩臺(tái)電腦來(lái)測(cè)試)

          我們發(fā)現(xiàn)conn.Close()并沒(méi)有執(zhí)行,但四次揮手還是發(fā)生了!

          查閱資料發(fā)現(xiàn)如下結(jié)論:

          a、b 兩個(gè)正常連接的對(duì)端進(jìn)程。假如 b 進(jìn)程沒(méi)有調(diào)用 close 就異常終止,那么發(fā)送 FIN 包是內(nèi)核 OS 代勞

          斷電/斷網(wǎng)時(shí)的連接是怎樣斷開(kāi)的

          我們通過(guò)上面的實(shí)驗(yàn)發(fā)現(xiàn)就算進(jìn)程異常終止,操作系統(tǒng)也會(huì)幫忙發(fā)起四次揮手

          但如果是斷電或斷網(wǎng)的情況下,操作系統(tǒng)就無(wú)法代勞了,這時(shí)會(huì)怎樣呢?為了便于測(cè)試,這里用兩臺(tái)電腦,client 連接 server,斷開(kāi) server 的網(wǎng)絡(luò)來(lái)模擬斷網(wǎng)斷電情況。

          可以肯定的是斷網(wǎng),斷電后,連接不會(huì)立即斷開(kāi),那么后續(xù)連接是否會(huì)斷開(kāi)呢?我們分成下面幾種情況來(lái)看

          斷網(wǎng)時(shí)有數(shù)據(jù)傳輸

          斷網(wǎng)時(shí)如果有數(shù)據(jù)發(fā)送,由于收不到 ACK,所以會(huì)重試,但并不會(huì)無(wú)限重試下去,達(dá)到一定的重發(fā)次數(shù)之后,如果仍然沒(méi)有任何確認(rèn)應(yīng)答返回,就會(huì)判斷為網(wǎng)絡(luò)或者對(duì)端主機(jī)發(fā)生了異常,強(qiáng)制關(guān)閉連接。此時(shí)的關(guān)閉是直接關(guān)閉,而沒(méi)有揮手(數(shù)據(jù)都發(fā)不出去,還揮啥手),Linux 下的設(shè)置為

          最小重傳時(shí)間是200ms ? ? ?最大重傳時(shí)間是120s ? ? ?重傳次數(shù)為15

          斷網(wǎng)時(shí)沒(méi)有數(shù)據(jù)傳輸

          斷網(wǎng)時(shí)如果沒(méi)有數(shù)據(jù)傳輸,還得看 TCP 連接的 KeepAlive 是否打開(kāi),關(guān)于 TCP 的 KeepAlive 簡(jiǎn)介如下:

          • TCP KeepAlive 是一種在不影響數(shù)據(jù)流內(nèi)容的情況下探測(cè)對(duì)方的方式,采用?;钣?jì)時(shí)器實(shí)現(xiàn),當(dāng)計(jì)時(shí)器被觸發(fā)時(shí),一端發(fā)送保活報(bào)文,另一端接收到報(bào)文后發(fā)送 ACK 響應(yīng)
          • 它并不是 TCP 的規(guī)范,但大部分的實(shí)現(xiàn)都提供了這一機(jī)制
          • 該機(jī)制存在爭(zhēng)議,有的人?;顧C(jī)制應(yīng)該在應(yīng)用程序中實(shí)現(xiàn)

          開(kāi)啟KeepAlive

          操作系統(tǒng)中有這么幾個(gè)參數(shù)控制 KeepAlive 的配置:

          • Keepalive_time:空閑時(shí)間,即多長(zhǎng)時(shí)間連接沒(méi)有發(fā)送數(shù)據(jù)時(shí)開(kāi)始 KeepAlive 檢測(cè)

          • Keepalive_intvl:發(fā)送間隔時(shí)間,即上述代碼的設(shè)置

          • Keepalive_probs:最多發(fā)送多少個(gè)檢測(cè)數(shù)據(jù)包

          在 Linux 上可以通過(guò)如下文件查看

          cat?/proc/sys/net/ipv4/tcp_keepalive_time
          cat?/proc/sys/net/ipv4/tcp_keepalive_intvl
          cat?/proc/sys/net/ipv4/tcp_keepalive_probes

          如果按照這個(gè)默認(rèn)值來(lái)看,得2小時(shí)沒(méi)有數(shù)據(jù)傳輸,KeepAlive 才開(kāi)始工作!

          而在 Go 中只有兩個(gè)參數(shù)可以設(shè)置:

          conn.SetKeepAlive(true)
          conn.SetKeepAlivePeriod(5?*?time.Second)

          其中第二個(gè) SetKeepAlivePeriod 源碼是這樣的:

          func?setKeepAlivePeriod(fd?*netFD,?d?time.Duration)?error?{
          ?//?The?kernel?expects?seconds?so?round?to?next?highest?second.
          ?secs?:=?int(roundDurationUp(d,?time.Second))
          ?if?err?:=?fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP,?sysTCP_KEEPINTVL,?secs);?err?!=?nil?{
          ??return?wrapSyscallError("setsockopt",?err)
          ?}
          ?err?:=?fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP,?syscall.TCP_KEEPALIVE,?secs)
          ?runtime.KeepAlive(fd)
          ?return?wrapSyscallError("setsockopt",?err)
          }

          SetKeepAlivePeriod 的參數(shù)同時(shí)設(shè)置了 tcp_keepalive_intvl 和 tcp_keepalive_time,tcp_keepalive_probes 沒(méi)法設(shè)置

          做個(gè)簡(jiǎn)單測(cè)試:client 開(kāi)啟 KeepAlive 連接 server 后,什么數(shù)據(jù)都不發(fā)送,把server 的網(wǎng)斷掉,可以看到 KeepAlive 心跳包,一段時(shí)間后連接被置為 CLOSED 狀態(tài)

          關(guān)閉KeepAlive

          關(guān)閉 KeepAlive 后,如果沒(méi)有數(shù)據(jù)傳輸,連接永遠(yuǎn)不會(huì)斷開(kāi)

          斷網(wǎng)后 server 重啟再恢復(fù)

          再思考一個(gè)場(chǎng)景,如果 client 與 server 建立連接后,沒(méi)有數(shù)據(jù)傳輸,斷掉 server 端的網(wǎng)絡(luò),這時(shí)如果把 server 程序重啟一下,再恢復(fù)網(wǎng)絡(luò),那這條連接還能用嗎?

          如果 server 重啟后,client 還是不發(fā)數(shù)據(jù),那這條連接看起來(lái)還是可用的,因?yàn)樗麄兏静恢缹?duì)方是個(gè)什么情況,但如果此時(shí) client 發(fā)送一點(diǎn)數(shù)據(jù)給 server,你會(huì)發(fā)現(xiàn) server 會(huì)發(fā)送一個(gè) RST 給client,然后 client 就斷開(kāi)連接了

          總結(jié)

          除了正常情況之外,本文從 TCP 連接斷開(kāi)的角度結(jié)合實(shí)驗(yàn)給出了一些結(jié)論:

          TCP 連接斷開(kāi)的揮手,在進(jìn)程崩潰時(shí),會(huì)由操作系統(tǒng)內(nèi)核代勞
          當(dāng) TCP 連接建立后,如果某一方斷電或斷網(wǎng),如果此時(shí)剛好正在發(fā)送數(shù)據(jù),TCP 數(shù)據(jù)包發(fā)送失敗后會(huì)重試,重試達(dá)到上限時(shí)也會(huì)斷開(kāi)連接
          當(dāng) TCP 連接建立后,如果某一方斷電或斷網(wǎng),且這條連接沒(méi)有數(shù)據(jù)傳輸時(shí)
          如果開(kāi)啟了 KeepAlive 則會(huì)在一定心跳檢測(cè)后斷開(kāi)連接,這個(gè)默認(rèn)檢測(cè)時(shí)間大概2個(gè)多小時(shí),比較久。如果未開(kāi)啟 KeepAlive 則連接永遠(yuǎn)存在
          如果一方發(fā)送 RST 包給另一方,也是會(huì)強(qiáng)制對(duì)方斷開(kāi)連接的

          1、Log4j2維護(hù)者吐槽沒(méi)工資還要挨罵,GO安全負(fù)責(zé)人建議開(kāi)源作者向公司收費(fèi)
          2、太難了!讓程序員崩潰的8個(gè)瞬間
          3、2021年程序員們都在用的神級(jí)數(shù)據(jù)庫(kù)
          4、Windows重要功能被閹割,全球用戶怒噴數(shù)月后微軟終于悔改
          5、牛逼!國(guó)產(chǎn)開(kāi)源的遠(yuǎn)程桌面火了,只有9MB 支持自建中繼器!
          6、摔到老三的 Java,未來(lái)在哪?
          7、真香!用 IDEA 神器看源碼,效率真高!

          點(diǎn)分享

          點(diǎn)收藏

          點(diǎn)點(diǎn)贊

          點(diǎn)在看

          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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热在线观看一区 | 香蕉人妻AV久久久久天天 |