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

          到底什么是網(wǎng)絡(luò)編程

          共 3080字,需瀏覽 7分鐘

           ·

          2021-01-15 21:22

          作者:蛇叔編程心法
          來源:SegmentFault 思否社區(qū)




          引言


          不知道大家有沒有這樣的經(jīng)歷,上網(wǎng)搜索技術(shù)文章,總是會(huì)看到網(wǎng)絡(luò)編程這個(gè)字眼,而各個(gè)互聯(lián)網(wǎng)大廠,也對(duì)掌握了網(wǎng)絡(luò)編程的人才,求賢若渴。其實(shí)網(wǎng)絡(luò)編程無處不在,我們平時(shí)用到的互聯(lián)網(wǎng)產(chǎn)品和網(wǎng)絡(luò)編程技術(shù)息息相關(guān)。掌握網(wǎng)絡(luò)編程,才能在繁雜的網(wǎng)絡(luò)世界中,看透問題本質(zhì),遇到網(wǎng)絡(luò)相關(guān)技術(shù)問題,也才能解決的游刃有余。


          目錄



          網(wǎng)絡(luò)協(xié)議棧


          那什么是網(wǎng)絡(luò)編程呢?有人說http就是網(wǎng)絡(luò)編程,有人說開發(fā)RPC框架是網(wǎng)絡(luò)編程,有人說嵌入式硬件相互通信是網(wǎng)絡(luò)編程,其實(shí)這些都涉及網(wǎng)絡(luò)編程,都脫離不了網(wǎng)絡(luò)協(xié)議棧。


          在大學(xué)課本《計(jì)算機(jī)網(wǎng)絡(luò)》中,我們學(xué)過網(wǎng)絡(luò)協(xié)議棧是OSI七層。現(xiàn)實(shí)中,大多數(shù)操作系統(tǒng),實(shí)現(xiàn)的是TCP/IP四層協(xié)議棧,如圖所示。



          而網(wǎng)絡(luò)編程就是在操作系統(tǒng)封裝的TCP/IP協(xié)議棧的基礎(chǔ)上,使用系統(tǒng)內(nèi)核暴露出來的socket網(wǎng)絡(luò)編程Api,進(jìn)行應(yīng)用程序開發(fā)。


          echo回顯服務(wù)器代碼




          #?編譯echo回顯服務(wù)器
          go?build?-o?echoServer?echoServer.go

          #?啟動(dòng)服務(wù)器
          ./echoServer


          使用nc,偽裝echoClient客戶端


          #?和服務(wù)器建立連接
          nc?127.0.0.1?8888

          #?客戶端
          hello-echo

          #?服務(wù)端
          hello-echo


          這時(shí)候,我們通過nc給服務(wù)器發(fā)送字符串,服務(wù)器會(huì)原樣把字符串回傳給我們。nc和echoServer整個(gè)交互過程是怎樣的呢?下圖展示詳細(xì)過程。


          網(wǎng)絡(luò)交互圖



          過程詳解


          眾所周知,TCP會(huì)有3次握手和4次揮手,我們的echo服務(wù)器就是基于TCP協(xié)議的。當(dāng)然少不了3次握手和4次揮手。


          服務(wù)器 | 建立socket內(nèi)核數(shù)據(jù)結(jié)構(gòu)


          • 首先,我們需要?jiǎng)?chuàng)建socket內(nèi)核數(shù)據(jù)結(jié)構(gòu),通過socket()系統(tǒng)調(diào)用,我們可以告訴內(nèi)核我們要建立基于ipv4的tcpsocket套接字,內(nèi)核會(huì)維護(hù)一個(gè)sock數(shù)據(jù)結(jié)構(gòu)并和一個(gè)文件相綁定,同時(shí)給我們返回一個(gè)socketfd,供后續(xù)函數(shù)使用。
          • 之后,我們需要通過bind(),告知內(nèi)核將哪個(gè)地址綁定到該socket內(nèi)核數(shù)據(jù)結(jié)構(gòu)。
          • 然后,使用listen()系統(tǒng)調(diào)用,將socket轉(zhuǎn)換為已監(jiān)聽套接字。此時(shí),服務(wù)器就可以進(jìn)行被動(dòng)連接了。


          客戶端 | 發(fā)起主動(dòng)連接;服務(wù)器 | 被動(dòng)連接


          • 如果此時(shí)客戶端通過connect()系統(tǒng)調(diào)用發(fā)起主動(dòng)連接,客戶端內(nèi)核協(xié)議棧,向服務(wù)器發(fā)送三次握手的第一步SYN。
          • 當(dāng)服務(wù)器收到這個(gè)SYN,會(huì)把該套接字放入半連接隊(duì)列,并向客戶端發(fā)送ACK、SYN。
          • 當(dāng)客戶端接受到這個(gè)ACK、SYN,并向服務(wù)器發(fā)送ACK。此時(shí)客戶端connect()系統(tǒng)調(diào)用返回,客戶端認(rèn)為三次握手完成。
          • 當(dāng)服務(wù)器收到客戶端傳來的ACK,則將內(nèi)核中的套接字放入全連接隊(duì)列,等待服務(wù)器調(diào)用accept,并返回給服務(wù)器。


          服務(wù)器 | 等待已連接套接字


          • 當(dāng)服務(wù)器調(diào)用accept(), 如果此時(shí)全連接隊(duì)列中沒有已完成三次握手的socket,則默認(rèn)會(huì)阻塞,直到全連接隊(duì)列中擁有已經(jīng)完成三次握手的socket。
          • accept()會(huì)為之前的監(jiān)聽套接字sock內(nèi)核數(shù)據(jù)結(jié)構(gòu),構(gòu)建一個(gè)新的文件,并分配新的fd,這個(gè)文件稱為已連接套接字。
          • 此時(shí),服務(wù)器和客戶端就可以收發(fā)數(shù)據(jù)了。


          服務(wù)器 | 客戶端 | 收發(fā)數(shù)據(jù)


          收發(fā)數(shù)據(jù)為何需要調(diào)用read和write呢?


          • 在內(nèi)核中,看到的acceptfd(已連接套接字),本質(zhì)和文件一樣,acceptfd就是文件描述符,所以我們可以直接使用read,write這種操作文件的系統(tǒng)調(diào)用。
          • linux內(nèi)核為每個(gè)已連接套接字,分配一個(gè)接受緩沖區(qū)和一個(gè)發(fā)送緩沖區(qū)。
          • 對(duì)于read(),是本機(jī)讀取acceptfd(服務(wù)器)或者socketfd(客戶端)相關(guān)的socket接收緩沖區(qū),如果socket接收緩沖區(qū)緩沖區(qū)中有數(shù)據(jù),read()返回, 否則read()會(huì)阻塞,直到socket接收緩沖區(qū)中擁有了對(duì)端數(shù)據(jù)。這個(gè)接受數(shù)據(jù)的過程是由內(nèi)核TCP/IP協(xié)議棧實(shí)現(xiàn)的。
          • 對(duì)于write(),寫入的是socket發(fā)送緩沖區(qū),如果此時(shí)發(fā)送緩沖區(qū)是滿的,write()則會(huì)被阻塞。寫入socket發(fā)送緩沖區(qū)的數(shù)據(jù),由內(nèi)核TCP/IP協(xié)議棧真實(shí)的發(fā)往對(duì)端。


          這里還有幾個(gè)點(diǎn):


          1. read返回大于0,表示讀取成功。
          2. read返回等于0,表示對(duì)端關(guān)閉連接,此時(shí)應(yīng)該調(diào)用Close關(guān)閉連接。
          3. read返回小于0,表示產(chǎn)生錯(cuò)誤。
          4. write返回小于0,頁表示產(chǎn)生錯(cuò)誤。


          客戶端 | 服務(wù)器 | 關(guān)閉連接


          • 在nc所在的終端上鍵入Ctrl+c,結(jié)束掉nc進(jìn)程。此時(shí)內(nèi)核協(xié)議棧,會(huì)給服務(wù)器發(fā)送FINTcp節(jié)。告知本端已經(jīng)關(guān)閉。
          • 服務(wù)端收到客戶端的FIN,內(nèi)核協(xié)議棧會(huì)給客戶端回復(fù)ACK,同時(shí)read()調(diào)用會(huì)返回0,這樣服務(wù)器就知道客戶端關(guān)閉連接了。
          • 這時(shí)候,服務(wù)器應(yīng)該調(diào)用close(),開啟四次揮手的第二個(gè)階段。向客服端發(fā)送一個(gè)FIN。
          • 當(dāng)客戶端收到該FIN后,內(nèi)核協(xié)議棧回復(fù)ACK,自身并進(jìn)入TIME_WAIT狀態(tài)。Linux下等待2MSL, 也就是60秒。


          至此,整個(gè)echoServer和nc的交互過程就講解完了。整個(gè)過程都是正常的網(wǎng)絡(luò)交互過程,很多異常的邊界沒有討論。在這里,主要是先帶大家打開網(wǎng)絡(luò)編程的神秘大門,有個(gè)初步映像,之后的文章我們會(huì)一步步對(duì)各種邊界異常情況進(jìn)行深入講解。


          網(wǎng)絡(luò)答疑


          1. 假如一方斷網(wǎng)了,不能進(jìn)行4次揮手,服務(wù)器會(huì)怎么處理?


          1. 默認(rèn)未斷開一端,會(huì)保持established狀態(tài)。
          2. 未斷開一端開啟了Keepalive,則會(huì)定期往對(duì)端發(fā)送TCP保活Segment,對(duì)端協(xié)議棧回復(fù)RST,未斷開端,就會(huì)知道已經(jīng)出現(xiàn)異常,關(guān)閉本端連接
          3. 如果未斷開一端沒有開啟Kepalive,則一直不知道對(duì)端已經(jīng)關(guān)閉,直到往對(duì)端寫數(shù)據(jù)會(huì)得到Connection reset by peer錯(cuò)誤,進(jìn)而知道對(duì)端已經(jīng)關(guān)閉。這種情況下,如果未斷開端再次往斷開對(duì)端寫數(shù)據(jù),則會(huì)產(chǎn)生EPIPE錯(cuò)誤。
          4. 所以通常需要服務(wù)器在應(yīng)用層做保活心跳,對(duì)這種情況的連接做定時(shí)踢掉處理。

          1. 客戶端發(fā)送FIN之后,會(huì)發(fā)生什么?


          • TCP是全雙工通信,也就是雙通道通信4次握手中,主動(dòng)關(guān)閉端(客戶端),發(fā)送FIN, 是告訴被動(dòng)關(guān)閉端(服務(wù)器)我不會(huì)再給你發(fā)送數(shù)據(jù)了,你不要再在acceptFd上讀取數(shù)據(jù)了,此時(shí)服務(wù)端再讀取數(shù)據(jù),會(huì)返回0, 也就是EOF。
          • 如果服務(wù)端不需要發(fā)送數(shù)據(jù)給客戶端,通常需要調(diào)用close, 服務(wù)端向客戶端發(fā)送FIN,也就是4次握手的第3步。
          • 如果服務(wù)端有數(shù)據(jù)要發(fā)送給客戶端,此時(shí)依舊可以通過socketfd發(fā)送數(shù)據(jù)給客戶端,這也是通常說的TCP半關(guān)閉狀態(tài)。


          后記


          通過之前的講解,我們其實(shí)可以發(fā)現(xiàn):TCP/IP協(xié)議和socketApi是緊密相關(guān)的。然而它們又有很多語義的差別。很多時(shí)候,想當(dāng)然的理解,并不是你所想的那樣。比如close()系統(tǒng)調(diào)用,會(huì)不會(huì)給對(duì)端發(fā)送FIN,要看該已連接套接字的引用計(jì)數(shù)是否達(dá)到0。如果有多個(gè)進(jìn)程共享這個(gè)文件,其中一個(gè)進(jìn)程close(),并不會(huì)給對(duì)端發(fā)送FINTcp節(jié)。諸如此類的細(xì)節(jié)還有很多很多。


          參考文獻(xiàn)


          1. 《TCP/IP詳解 卷1》
          2. 《Unix網(wǎng)絡(luò)編程 卷1》
          3. 《計(jì)算機(jī)網(wǎng)絡(luò)》



          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動(dòng)和交流。

          -?END -

          瀏覽 35
          點(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>
                  超碰人人操人人爽 | 国模三区| 一区二区在线不卡 | 欧美高潮AAAAAA片 | 丁香五月婷婷在线观看 |