一、說明
直接的原因是昨晚《計算機網絡(自頂向下方法)》到貨了,以為能講得有些不一樣,但看完整本也就是老調地講過來講應用層、傳輸層、網絡層、網絡接口層。感覺比之謝希仁的《計算機網絡》還有不如。
看了很多關於網絡模型的文章,總感覺部是在講原理原理也講得挺明白,但個人覺得就是看完后不能讓讀者明白這個東西在編程上是什么、在編程上要怎么實現。
二、各網絡模型定義
2.1 定義
ISO/OSI七層網絡參考模型:
應用層 |
表示層 |
會話層 |
傳輸層 |
網絡層 |
數據鏈路層 |
物理層 |
TCP/IP四層網絡模型:
應用層 |
傳輸層 |
網絡層 |
網絡接口層 |
教學五層網絡模型:
應用層 |
傳輸層 |
網絡層 |
數據鏈路層 |
物理層 |
2.2 各網絡模型之間的關系
1. TCP/IP就相當於把ISO/OSI的應用層、表示層、會話層壓縮為應用層;把數據鏈路層、物理層合並為網絡接口層(更簡單點網絡接口層可以認為就是網卡)。壓縮傾向於說把表示層和會話層的頭部給砍掉了,然后其功能也部份砍掉了,合並傾向於說物理層的頭部砍掉了但要實現的功能還是實現的。
2. 教學五層網絡模型相當於把TCP/IP的網絡接口層,恢復成數據鏈路層和物理層兩個層;注意教學五層網絡模型在工業界是沒有的(也就是沒有過五層網絡模型對應的產品),這個模型只是為了教育界方便教學弄出來的。注意關系,五層模型是從四層模型來的不是從七層模型來的,也不是七層模型從五層模型來的。
3. 為什么說教學五層網絡模型方便教學,物理層真沒什么感覺啊?我對物理層也沒有准確的定義,我覺得高電平、低電平、上升沿、下降沿分別表示什么,波分復用、碼分多址,電壓電流大小,插口形狀大小等應該都是物理層的工作。
4. 當前TCP/IP占領了市場,不是因為TCP/IP先被工業界應用,ISO/OSI才被學術界提出來的;而是OSI/ISO最先提出來,而且聯合了各大廠商大家都基本同意以其為標准來進行生產但都沒什么動靜,然后TCP/IP越搞越火大家都去搞TCP/IP了。所以說,按我理解的話ISO/OSI確實是被TCP/IP正面擊敗的。
5. 當年上計算機網絡課的時候,老師對ISO/OSI已被淘汰的說法嗤之以鼻並說網絡最終一定會向ISO/OSI方向發展。當時感到不解,現在想來老師可能是指SSL的發展,但如果是指SSL的發展的話那其斷言也就不准確。從功能上說ISO/OSI就相當於被砍了表示層和會話層,我總感覺各種對這兩個層功能的定義似是而非,表示層有“解密”類似的關鍵字,但要說當時標准的制定者預期會話層和表示層的完成任務就是當前SSL層所做的工作,鑒於多處可見的“制定者沒有預想要網絡會在世界普及所以只使用了32位IP地址”的說法以及后來的http、ftp都沒太考慮安全性,ISO/OSI制定者則有遠超別人的預見性是不太能令人相信的。也就是說個人認為,SSL是方向,但SSL並不就是ISO/OSI的表示層、會話層。
6. 對於ISO/OSI為什么會被TCP/IP所擊敗,我並沒有深入去了解當明的情況只是個人推測。首先說物理層學術界直覺上需要一個物理層頭部工業界發現使用網卡后完全沒必要去管物理層,所以工業界就將物理層頭部給砍了。然后再是表示層和會話層,可能是學術界自己不生產不能給工業界清楚地描述這兩個層在編程上是要怎么實現,而工業界自己又發現到傳輸層之后信息就傳到進程中工業界自己想怎么玩怎么玩就不去管學術界復雜又說得不清不楚的那一套,正如有人和你說你到那后要左拐然后右拐然后再左拐但你自己到那后發現直直走進去就可以了,你就直接走進去了。總的而言就是學術界過於理想化、脫離實際生產(當然根本上來說工業界跨越性的進步還是仰仗學術的進步,當如果學術界自己已搞不了生產再沒有理論的引領作用那就真屍位素餐了)。
7. SSL最經典的實現是https,但是本質上ssl就是在傳輸層和應用層之間插入的一個新層,SSL和https並沒有必然的綁定關系,SSL層之上完全可以是其他任何應用層協議,比如ftp、telnet、smtp、pop、imap等等。
8. IOT宣稱將網絡簡化成感知層、網絡層和應用層,對IOT數據包我也只是一知半解,但我一直認為如果條件允許標准化才是趨勢優勢不明顯的專用方案終會被淘汰。具體到IOT而言,如果硬件條件允許那么IOT的系統將Linux化,網絡將TCP/IP化。至少當前從手機到安防到汽車這些代表性行業都在呈現這種趨勢。
9. 因為我們實現一個服務監聽使用的是socket,而socket就是tcp/ip,所以客戶端訪問也需要使用tcp/ip協議--電腦客戶端訪問服務時要使用tcp/ip協議手機客戶端訪問時亦需要tcp/ip協議。所以不管是3G、4G、5G不管他們多復雜,他們啟的作用就是“物理層+數據鏈路層”的作用,亦即把數據匯總到一個類似路由器的網關,進而實現手機與因特網的通信。WiFi等也一個意思。
三、編程角度的理解
3.1 傳統的網絡解說
以太網頭部是這樣的:
ip頭部是這樣的:
tcp頭部是這樣的:
各層具有的協議是這樣的:
整個網絡傳輸過程是這樣的:
3.2 網絡協議理解
上一節的講解如果是已經知道各頭部在數據包中長什么樣的人看,應該會感覺各要點都已涉及到了,講的不錯。但就個人而言,當時學完計算機網絡后要進行網絡編程時很是害怕,因為在編程上自己又完全不懂怎么實現。
講封裝的文章會給出下邊這個圖,這和我想表達的意思是一樣的,但感這圖還是不太夠直接。
3.2.1 從存儲角度對網絡協議的理解
從存儲角度,數據包可以認為就是一個字符串數組。比如一個幀長為200字節,我們稱之為strBuffer[200],各層對應數組中的位置如下
頭部 | 對應字符串數組位置 |
以太頭 | strBuffer[0-13]、strBuffer[196-199] |
ip頭 | strBuffer[14-33] |
tcp頭 | strBuffer[34-53] |
應用層 | strBuffer[54-195] |
為了增強對數據包就只不過是個字符串數組這個觀點,我們來看一個wireshark截獲的http數據包,高亮處是ip頭其前14個字節是以太頭,后20個字節是tcp頭,再后邊就是http層數據
3.2.2 從編程角度實現網絡協議
仍以上邊str[200]來說明,當我們要從電腦向外發一個幀,其本質工作就是把以太頭信息填到strBuffer[0-13]和strBuffer[196-199],把ip頭信息填到strBuffer[14-33],把tcp頭信息填到strBuffer[34-53],再把應用層內容填到strBuffer[54-195],最后把這個數組發出去。總結而言就是填充字符串數組,然后把字符串數組發出去。
逐字節填充是可以但那會相當費勁,通常而言我們會給各協議頭部定義一個結構(struct),然后定義一個結構對應的變量,然后填充該變量,最后將變量使用strcpy等函數將頭部復制到其對應在字符串數組中的位置即可。
//ip頭部,共20字節 typedef struct iphdr{ UCHAR ver_pack; //4位版本號+4位ip頭長度 UCHAR tos; //8位tos USHORT total_len; //16位tcp頭+應用層長度 USHORT ident; //16位 USHORT frag_and_flags; //16位 UCHAR ttl; //8位ttl UCHAR proto; //8位上層協議號 USHORT checksum; //8位ip頭部校驗和 UINT sourceIP; //32位源ip地址 UINT desrIP; //32位目的ip地址 }IPHEADER,*PIPHEADER; //tcp頭部,其20字節 typedef struct tcphdr //定義TCP首部 { USHORT th_sport; //16位源端口 USHORT th_dport; //16位目的端口 unsigned int th_seq; //32位序列號 unsigned int th_ack; //32位確認號 unsigned char th_lenres; //4位首部長度+6位保留字 unsigned char th_flag; //6位標志位 USHORT th_win; //16位窗口大小 USHORT th_sum; //16位校驗和 USHORT th_urp; //16位緊急數據偏移量 }TCPHEADER;
比如ip頭部就是:定義一個ip頭部變量:IPHEADER ip_header----填充ip_header各成員----strcpy(strBuffer+14,ip_header)。
其他以太頭、tcp頭等等都是類似操作的。
3.2.3 應用層和其他層在編碼上的區別
經過前邊的介紹,以太頭、ip頭、tcp頭應該比較清楚了,但自己學習時感覺還有一個隔閡,就是應用層為什么和其他網絡接口層、ip層、tcp層那么不一樣。
為什么同是復制到同一個字符串數組內其他三層需要結構、需要填一堆二進制的東西,而應用層strBuffer += "hello client\r\n"就完事了。
造成這樣的原因主要是因為,以太頭、ip頭、tcp頭用數值編碼,而應用層上使用的是ascii編碼。比如1024這個數,如果在以太頭、ip頭、tcp頭那么會被編碼成0x1000,而如果是在應用層那就會編碼成0x31303134。
至於為什么以太頭、ip頭、tcp頭使用數值編碼而不使用ascii編碼,那應該是方便運算。這些頭部從生成到傳輸過程其主要都是用於進行加減、比較、求反等數值運算,而數值運算上到機器中最終還是得用數值編碼的,使用ascii編碼既耗費了長度運算時還得轉化一次那是對機器相當不友好的。另外一般的網絡編程並不需要去修改這些頭部,對人不友好一點是沒很影響的。
至於為什么應用層不使用數值編碼,首先應用層傳輸更多的是字符串而不是數值,即便要傳數值比如傳個1024你覺得要你自己把數轉成十六進制寫進去方便還是接收方int(str)方便?