1.常規(guī)的網(wǎng)絡(luò)交互過程是從客戶端發(fā)起網(wǎng)絡(luò)請求,用戶態(tài)的應(yīng)用程序(瀏覽器)會(huì)生成 HTTP 請求報(bào)文、并通過 DNS 協(xié)議查找到對應(yīng)的遠(yuǎn)端 IP 地址。 2.在套接字生成之后進(jìn)入內(nèi)核態(tài),瀏覽器會(huì)委托操作系統(tǒng)內(nèi)核協(xié)議棧中的上半部分,也就是 TCP/UDP 協(xié)議發(fā)起連接請求。 3.然后經(jīng)由協(xié)議棧下半部分的 IP 協(xié)議進(jìn)行封裝,使數(shù)據(jù)包具有遠(yuǎn)程定位能力。 4.經(jīng)過 MAC 層處理,找到接收方的目標(biāo) MAC 地址。 5.最終數(shù)據(jù)包在經(jīng)過網(wǎng)卡轉(zhuǎn)化成電信號經(jīng)過交換機(jī)、路由器發(fā)送到服務(wù)端,服務(wù)端經(jīng)過處理拿到數(shù)據(jù),再通過各種網(wǎng)絡(luò)協(xié)議把數(shù)據(jù)響應(yīng)給客戶端。 6.客戶端拿到數(shù)據(jù)進(jìn)行渲染 7.客戶端和服務(wù)端之間反復(fù)交換數(shù)據(jù),客戶端的頁面數(shù)據(jù)就會(huì)發(fā)生變化。剛才的過程中,提到了多個(gè)層級和網(wǎng)絡(luò)協(xié)議,那么網(wǎng)絡(luò)為什么要分層呢?網(wǎng)絡(luò)協(xié)議又是什么? 前置知識:網(wǎng)絡(luò)分層和網(wǎng)絡(luò)協(xié)議在計(jì)算機(jī)網(wǎng)絡(luò)時(shí)代初期,各大廠商推出了不同的網(wǎng)絡(luò)架構(gòu)和標(biāo)準(zhǔn),為統(tǒng)一標(biāo)準(zhǔn),國際標(biāo)準(zhǔn)化組織 ISO 推出了統(tǒng)一的 OSI 參考模型。 當(dāng)前網(wǎng)絡(luò)主要遵循的 IEEE 802.3 標(biāo)準(zhǔn),就是基于 OSI 模型提出的,主要定義的是物理層和數(shù)據(jù)鏈路層有線物理數(shù)據(jù)流傳輸?shù)臉?biāo)準(zhǔn)。 那網(wǎng)絡(luò)為什么要分層? 通過分層處理簡化問題難度,降低復(fù)雜度,由于分層后的各層之間相互獨(dú)立,我們可以把大問題分割成小問題。同樣,分層也保證了網(wǎng)絡(luò)的松耦合和相對的靈活,分層拆分后易于各層的實(shí)現(xiàn)和維護(hù),也方便了各層的后續(xù)擴(kuò)展。網(wǎng)絡(luò)分層解決了網(wǎng)絡(luò)復(fù)雜的問題,在網(wǎng)絡(luò)中傳輸數(shù)據(jù)中,我們對不同設(shè)備之間的傳輸數(shù)據(jù)的格式,需要定義一個(gè)數(shù)據(jù)標(biāo)準(zhǔn),所以就有了網(wǎng)絡(luò)協(xié)議。網(wǎng)絡(luò)協(xié)議是雙方通信的一種約定,以便雙方都可以理解對方的信息。接下來我們就用 OSI 協(xié)議體系中廣泛應(yīng)用的 TCP/IP 層的體系結(jié)構(gòu)來分析整個(gè)過程,需要重點(diǎn)關(guān)注數(shù)據(jù)處理的過程和網(wǎng)絡(luò)協(xié)議
發(fā)起請求階段(應(yīng)用層)先來看下網(wǎng)絡(luò)應(yīng)用層的工作流程,依然以瀏覽器中輸入U(xiǎn)RL開始。 用戶輸入:在瀏覽器中輸入U(xiǎn)RL在瀏覽器中輸入U(xiǎn)RL,瀏覽器會(huì)根據(jù)輸入內(nèi)容,先匹配對應(yīng)的URL以及關(guān)鍵字,給出輸入建議,同時(shí)校驗(yàn)URL的合法性,并且會(huì)在URL前后補(bǔ)全URL 以輸入 為例,首先瀏覽器會(huì)判斷出這是一個(gè)合法的 URL,并且會(huì)補(bǔ)全為 http://www.。 其中 http 為協(xié)議, 為網(wǎng)絡(luò)地址,每個(gè)網(wǎng)絡(luò)欄的地址都符合通用 URI 的語法。URI 一般語法由五個(gè)分層序列組成。后面的第一行內(nèi)容我給你列了 URL 的格式,第二行做了行為說明。 URI = scheme:[//authority]path[?query][#fragment] URI = 方案:[//授權(quán)]路徑[?查詢][#片段ID] 接著,瀏覽器從 URL 中會(huì)提取出網(wǎng)絡(luò)的地址,也叫做主機(jī)名(host),一般主機(jī)名可以為域名或 IP 地址,此處使用域名。 網(wǎng)絡(luò)請求前:查看瀏覽器緩存瀏覽器在 HTTP 報(bào)文生成完成后,它并不是馬上就開始網(wǎng)絡(luò)請求的。 在請求發(fā)出之前,瀏覽器首先會(huì)檢查保存在本地計(jì)算機(jī)中的緩存,如果訪問過當(dāng)前的 URL,會(huì)先進(jìn)入緩存中查詢是否有要請求的文件。此時(shí)存在的緩存有路由器緩存、DNS 緩存、瀏覽器緩存、Service Worker、Memory Cache、Disk Cache、Push Cache、系統(tǒng)緩存等。 struct hostent { char *h_name;// 主機(jī)的別名.www.就是google他自己的別名 char **h_aliases;// 主機(jī)ip地址的類型,到底是ipv4(AF_INET),還是pv6(AF_INET6) int h_addrtype;// 主機(jī)ip地址的長度 int h_length;// 主機(jī)ip地址的長度 char **h_addr_list; // 主機(jī)的ip地址,注意,這個(gè)是以網(wǎng)絡(luò)字節(jié)序存儲(chǔ)的 #define h_addr h_addr_list[0] 這個(gè)函數(shù),是將類型為af的網(wǎng)絡(luò)地址結(jié)構(gòu)src,轉(zhuǎn)換成主機(jī)序的字符串形式,存放在長度為cnt的字符串中。返回指向dst的一個(gè)指針。如果函數(shù)調(diào)用錯(cuò)誤,返回值是NULL }; 如果沒有訪問過當(dāng)前的 URL,就會(huì)跳過緩存這一步,這時(shí)我們就會(huì)進(jìn)入網(wǎng)絡(luò)操作了。 域名解析:DNS接著上一小節(jié)在瀏覽器確認(rèn)了輸入的 URL 之前沒有訪問,瀏覽器就會(huì)生成對應(yīng)的 HTTP 請求,這時(shí)瀏覽器需要委托操作系統(tǒng)將 HTTP 報(bào)文發(fā)送到對應(yīng)的服務(wù)端。在發(fā)送消息之前,還有一個(gè)工作需要做,就是查找服務(wù)端的 IP 地址,因?yàn)椴僮飨到y(tǒng)在發(fā)送消息時(shí),必須知道對方的 IP 地址才可以發(fā)送。 但是由于 IP 地址由一串?dāng)?shù)字組成,不夠語義化,為方便你記憶,我們將 IP 地址映射為域名,于是就有這樣一個(gè)服務(wù),維護(hù)了 IP 和域名的映射關(guān)系,它就是非常重要的基礎(chǔ)設(shè)施——DNS 服務(wù)器。DNS 服務(wù)器是一個(gè)分布式數(shù)據(jù)庫,分布在世界各地。 為提高效率,DNS 是按照一定的結(jié)構(gòu)進(jìn)行組織的,不同層次之間按照英文句點(diǎn). 來分割。 在域名中,我們的層級關(guān)系是按照從左到右、從低到高排列的,不同層級由低到高維護(hù)了一個(gè)樹形結(jié)構(gòu),最高一級的根節(jié)點(diǎn)為 root 節(jié)點(diǎn),就是我們所謂的根域名服務(wù)器,因此 完整的域名應(yīng)該是 .,后面的 . 相當(dāng)于.root。 但是所有域名的頂級域名都一樣,因此被省略;再下一級.com 為頂級域名;再下一級的 cosmos 為權(quán)威域名。 DNS解析 > 瀏覽器DNS緩存 > hosts文件 > 本地DNS服務(wù)器 > ISP DNS服務(wù)器 操作系統(tǒng)協(xié)議棧(傳輸層和網(wǎng)絡(luò)層)已經(jīng)根據(jù) URL 拿到需要請求的唯一地址了,接下來就要委托操作系統(tǒng)將 HTTP 報(bào)文發(fā)送出去了,這個(gè)過程由操作系統(tǒng)中的協(xié)議棧負(fù)責(zé)處理。 TCP/IP 協(xié)議棧是現(xiàn)在使用最廣泛的網(wǎng)絡(luò)協(xié)議棧,Internet 就是建立在 TCP/IP 協(xié)議棧基礎(chǔ)上的。除 TCP/IP 協(xié)議棧外,我們的操作系統(tǒng)內(nèi)核可以支持多個(gè)不同的協(xié)議棧,如后續(xù)我們將會(huì)用到的 LwIp。 協(xié)議棧內(nèi)部分為幾部分,分別承擔(dān)著不同的作用。 協(xié)議棧的上半部分負(fù)責(zé)和應(yīng)用層通過套接字(Socket)進(jìn)行交互,它可以是 TCP 協(xié)議或 UDP 協(xié)議。應(yīng)用層會(huì)委托協(xié)議棧的上部分完成收發(fā)數(shù)據(jù)的工作 協(xié)議棧的下半部分則負(fù)責(zé)把數(shù)據(jù)發(fā)送給到指定方的 IP 協(xié)議,由 IP 協(xié)議連接下層的網(wǎng)卡驅(qū)動(dòng)。 可靠性傳輸:建立 TCP 連接瀏覽器通過 DNS 解析拿到 Cosmos 的 IP 地址后, 瀏覽器取出 URL 的端口(HTTP 默認(rèn) 80,HTTPS 默認(rèn) 443)。隨即瀏覽器會(huì)委托操作系統(tǒng)協(xié)議棧的上半部分創(chuàng)建新的套接字(Socket)向?qū)?yīng)的 IP 發(fā)起 TCP 連接請求。
1.首先瀏覽器作為客戶端會(huì)發(fā)送一個(gè)小的 TCP 分組,這個(gè)分組設(shè)置了一個(gè)特殊的 SYN 標(biāo)記,用來表示這是一條連接請求。同時(shí)設(shè)置初始序列號為 x 賦值給 Seq (這次捕獲組的數(shù)據(jù)為: SYN=1, Seq=1)。 2.服務(wù)器接受到客戶端的 SYN 連接后,會(huì)選擇服務(wù)器初始序號 y。同時(shí)向客戶端發(fā)送含有連接確認(rèn)(SYN+ACK)、Seq=0(本例中的服務(wù)器初始序號)、Ack=1(客戶端的序號 x +1)等信息的 TCP 分組。 3.客戶端收到了服務(wù)器的確定字段后,向服務(wù)器發(fā)送帶有 ACK=1、Seq=1 (x+1)、Ack=1 (服務(wù)器 Ack 信息的拷貝)等字段的 TCP 分組給服務(wù)器。即使是發(fā)送一個(gè) TCP 分組,也是一次網(wǎng)絡(luò)通信,那么對于 TCP 層來說,這一次通信的數(shù)據(jù)前面就要包含一個(gè) TCP 包頭,向下層表明這是個(gè) TCP 數(shù)據(jù)包。TCP 包頭其實(shí)是一個(gè)數(shù)據(jù)結(jié)構(gòu),我為你準(zhǔn)備了一幅圖,以便理解。
其次,你需要注意的是一串有序數(shù)字 Sequence number,這個(gè)序號保證了 TCP 報(bào)文是有序被接受的,解決網(wǎng)絡(luò)包的亂序問題。 之后的 Acknowledgement number 是確認(rèn)號,只有對方確認(rèn)收到,否則會(huì)一直重發(fā),這個(gè)是防止數(shù)據(jù)包丟失的。 緊接著還有一些狀態(tài)位,由于 TCP 是有狀態(tài)的,是用于維護(hù)雙方連接的狀態(tài),狀態(tài)發(fā)生變更會(huì)更新雙方的連接狀態(tài)。后面還有一個(gè),窗口大小 Window Size,用于流量控制。 TCP 層封裝好了數(shù)據(jù)包,會(huì)將這個(gè) TCP 數(shù)據(jù)包向下層發(fā)送,而 TCP 層的下層就是 IP 層,下面我們一起去瞧一瞧完成目的地定位的 IP 層。 目的地定位:IP層在IP協(xié)議里面需要有源地址IP和目標(biāo)地址IP: 源地址IP,就是客戶端輸出的IP地址 目標(biāo)地址IP,即通過DNS域名解析得到的web服務(wù)器ip 因?yàn)镠TTP是經(jīng)過TCP傳輸?shù)?,所以IP包頭的協(xié)議號,要填寫06,表示協(xié)議為TCP 假設(shè)客戶端有多個(gè)網(wǎng)卡,就會(huì)有多個(gè)IP地址,那IP頭部的源地址應(yīng)該選擇哪個(gè)IP呢? 多個(gè)網(wǎng)卡需要填寫源地址時(shí),需要判斷應(yīng)該是用哪個(gè)一塊網(wǎng)卡來發(fā)送包。 這個(gè)時(shí)候就需要路由表規(guī)則,來判斷哪一個(gè)網(wǎng)卡作為源地址IP 在Linux中可以 使用route -n 命令查看當(dāng)前系統(tǒng)的路由表 根據(jù)上邊的路由表 假設(shè)Web服務(wù)器的目標(biāo)地址是192.168.10.200 TCP在維護(hù)狀態(tài)的過程中,都需要委托 IP 層將數(shù)據(jù)封裝,發(fā)送和處理網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)入網(wǎng)絡(luò)層。IP 協(xié)議是 TCP/IP 協(xié)議棧的核心,IP 協(xié)議中規(guī)定了在 Internet 上進(jìn)行通信時(shí)應(yīng)遵循的規(guī)則,包括 IP 數(shù)據(jù)包應(yīng)如何構(gòu)成、數(shù)據(jù)包的路由等,而 IP 層實(shí)現(xiàn)了網(wǎng)絡(luò)上的點(diǎn)對點(diǎn)通信。 首先來看看 IP 層處理上層網(wǎng)絡(luò)數(shù)據(jù)包的過程,網(wǎng)絡(luò)數(shù)據(jù)包(無論輸入數(shù)據(jù)包還是輸出數(shù)據(jù)包)進(jìn)入網(wǎng)絡(luò)層后,IP 層協(xié)議的函數(shù)都要對網(wǎng)絡(luò)數(shù)據(jù)包做后面這 5 步操作。 1、數(shù)據(jù)包校驗(yàn)和檢驗(yàn) 2、防火墻對數(shù)據(jù)包過濾 3、IP 選項(xiàng)處理 4、數(shù)據(jù)分片和重組 5、接收、發(fā)送和前送 為了完成上述操作,IP 層被設(shè)計(jì)成三個(gè)部分,分別是 IP 尋址、路由和分包組包。 其實(shí)在網(wǎng)絡(luò)通信的過程中,每個(gè)設(shè)備都必須擁有自己的 IP 地址才可以完成通信,我們的** IP 地址是以四組八位的組合進(jìn)行約定,每組以. 號隔開,再轉(zhuǎn)化為十進(jìn)制的方式。這里要注意,IP 地址并不是以主機(jī)數(shù)目進(jìn)行配置的,而是根據(jù)網(wǎng)卡數(shù)**來進(jìn)行。 有了 IP 地址,就可以通信了,但 IP 層仍然是一個(gè)軟件實(shí)現(xiàn)的功能邏輯層,那它如何完成通信呢,答案是不能直接完成通信,它只是把 IP 地址及相關(guān)信息組裝成一個(gè) IP 頭,把這個(gè) IP 頭放在網(wǎng)絡(luò)數(shù)據(jù)的前面,形成了 IP 包,最后把這個(gè) IP 包發(fā)送給 IP 層的下一層組件就行了,IP 頭的格式如下所示。 有了 IP 頭的網(wǎng)絡(luò)數(shù)據(jù),就有了發(fā)送目的地的信息,那么該如何才能將報(bào)文發(fā)送到目的地呢?這就要請 MAC 出場了,這個(gè) MAC 層,就是 IP 層的下一層組件 兩點(diǎn)傳輸 —— MAC 生成了IP頭部之后,接下來網(wǎng)絡(luò)包需要在IP頭部加上MAC頭部 MAC包頭里面有發(fā)送方MAC地址 接收方MAC地址 用于兩點(diǎn)之間的傳輸 MAC包頭的協(xié)議類型只有 IP協(xié)議(0800) ARP協(xié)議(0806) 發(fā)送方的MAC地址可以直接讀出來 接收方的MAC地址 需要通過ARP協(xié)議幫助我們路由器的MAC地址 ARP協(xié)議會(huì)在以太網(wǎng)中以廣播的形式 ,對以太網(wǎng)所有設(shè)備喊出,這個(gè)IP地址是誰的,請把你的MAC地址告訴我 對方回答之后,如果對方和自己處于同一個(gè)子網(wǎng)中,就可以將獲得的MAC地址寫入頭部 之后會(huì)把查詢結(jié)果放到一塊ARP緩存的內(nèi)存 所以說: 先查詢ARP緩存,如果其中已經(jīng)保存了對方的MAC地址,就不需要發(fā)送ARP查詢,可以直接使用ARP緩存中的地址 而當(dāng)ARP緩存中不存在對方的MAC地址時(shí),則發(fā)送了ARP廣播查詢 linux中可以通過arp -a 之后獲得了MAC頭部的數(shù)據(jù)包就可以開始走啦 發(fā)送方的 MAC 頭比較容易獲取,讀取當(dāng)前設(shè)備網(wǎng)卡的 MAC 地址就可以獲取,而接收方的 MAC 頭則需要通過 ARP 協(xié)議在網(wǎng)絡(luò)中攜帶 IP 地址,在一個(gè)網(wǎng)絡(luò)中發(fā)送廣播信息,這樣就能獲取這個(gè)網(wǎng)絡(luò)中的 IP 地址對應(yīng)的 MAC 地址,然后就能給我們的 IP 包加上 MAC 頭了,最后這個(gè)加上 MAC 頭的 IP 包,成為一個(gè) MAC 數(shù)據(jù)包,就可以準(zhǔn)備發(fā)送出去了 下面就進(jìn)入最后的階段,數(shù)據(jù)的發(fā)送,即網(wǎng)絡(luò)層中的最低層——物理層 電信號的出口:網(wǎng)卡(物理層)現(xiàn)在拿到了經(jīng)過層層處理過的數(shù)據(jù)包,數(shù)據(jù)包只是一串二進(jìn)制數(shù)據(jù),網(wǎng)絡(luò)上的數(shù)據(jù)傳送,是依賴電信號的,所以我們現(xiàn)在需要將數(shù)據(jù)包轉(zhuǎn)化為電信號,才能在物理的網(wǎng)線上面?zhèn)鬏敗?/span> 那么數(shù)據(jù)包是如何被轉(zhuǎn)換電信號的呢 互相扒皮——服務(wù)器和客戶端 數(shù)據(jù)包到了之后,看MAC是否符合,符合就收起來, 扒開IP的頭,發(fā)現(xiàn)IP地址符合,根據(jù)IP頭中的協(xié)議項(xiàng),知道上層是TCP 扒開TCP的頭,里面有序列號,需要看一看這個(gè)是不是我想要的,如果是就放入緩存中返回一個(gè)ACK,如果不是就丟棄。TCP頭部還有端口號,HTTP的服務(wù)器正在監(jiān)聽這個(gè)端口號。 于是服務(wù)器就知道HTTP進(jìn)程要這個(gè)包,于是就把這個(gè)包發(fā)給HTTP進(jìn)程。 服務(wù)器的HTTP進(jìn)程看到,原來這個(gè)請求是要訪問一個(gè)頁面,于是就把網(wǎng)頁封裝在HTTP相應(yīng)報(bào)文里。 之后相應(yīng)報(bào)文也要穿上TCP IP MAC頭部,不過這次是源地址時(shí)服務(wù)器的IP地址,目的地址是客戶端的IP地址。 現(xiàn)在,數(shù)據(jù)終于通過網(wǎng)卡離開了計(jì)算機(jī),進(jìn)入到局域網(wǎng),通過局域網(wǎng)中的設(shè)備,集線器、交換機(jī)和路由器等,數(shù)據(jù)會(huì)進(jìn)入到互聯(lián)網(wǎng),最終到達(dá)目標(biāo)服務(wù)器。 接著,服務(wù)器就會(huì)先取下數(shù)據(jù)包的 MAC 頭部,查看是否匹配自己 MAC 地址。然后繼續(xù)取下數(shù)據(jù)包的 IP 頭,數(shù)據(jù)包中的目標(biāo) IP 地址和自己的 IP 地址匹配,再根據(jù) IP 頭中協(xié)議項(xiàng),知道自己上層是 TCP 協(xié)議。 之后,還要繼續(xù)取下數(shù)據(jù)包 TCP 的頭。完成一系列的順序校驗(yàn)和狀態(tài)變更后,TCP 頭部里面還有端口號,此時(shí)我們的 HTTP 的 server 正在監(jiān)聽這個(gè)端口號,就把數(shù)據(jù)包再發(fā)給對應(yīng)的 HTTP 進(jìn)程。 HTTP 進(jìn)程從服務(wù)器中拿到對應(yīng)的資源(HTML 文件),再交給操作系統(tǒng)對數(shù)據(jù)進(jìn)行處理。然后再重復(fù)上面的過程,層層攜帶 TCP、IP、MAC 頭部。接下來數(shù)據(jù)從網(wǎng)卡出去,到達(dá)客戶端,再重復(fù)剛才的過程拿到相應(yīng)數(shù)據(jù)??蛻舳四玫綄?yīng)的 HTML 資源,瀏覽器就可以開始解析渲染了,這步操作完成后,用戶最終就能通過瀏覽器看到相應(yīng)的頁面。 畫了兩幅圖,來描述上述過程,第一幅是網(wǎng)絡(luò)協(xié)議各層之間封裝與拆封數(shù)據(jù)的過程,如下所示 下面的第二幅圖,是描述客戶端與服務(wù)器之間用網(wǎng)絡(luò)協(xié)議連接通信的過程,如下所示 此時(shí)客戶端和服務(wù)端之間通過 TCP 協(xié)議維護(hù)了一個(gè)連接狀態(tài),如果客戶端需要關(guān)閉網(wǎng)絡(luò),那么會(huì)進(jìn)行四次揮手,兩邊的網(wǎng)絡(luò)傳輸過程至此完成。 |
|