第3章TCP/IP協定簇
3.1TCP/IP協定簇介紹
TCP(Transmission Control Protocol)/IP(Internet Protocol)協定是現代網際網路的基石,其實TCP/IP代表一組協定,稱為TCP/IP協定簇,TCP和IP隻是其中的兩個最重要的協定。TCP/IP協定簇使得由異構硬體和軟體系統組成的主機進行互聯,進而建構起全球網際網路大廈。
由于主機間進行通信的過程十分複雜,是以,經常将主機間通信的過程劃分為相對獨立的不同層次,每層實作相對獨立的一定功能,上層通過标準接口調用下層提供的功能而不關心具體的細節,以簡化主機間通信協定的設計。ISO(International Standardization Organization)推出的OSI(Open System Interconnect Reference Model)協定是一種參考協定,分為七層,從下到上分别為實體層、資料鍊路層、網絡層、傳輸層、會話層、表示層、應用層。TCP/IP協定簇參考ISO/OSI實作。
TCP/IP協定中的TCP協定最早由斯坦福大學的兩名研究人員于1973年提出,為實作主機間通過TCP通信,逐漸衍生出其他協定,最終形成TCP/IP協定族。1983年TCP/IP協定簇被UNIX 4.2BSD系統采用,随着UNIX的成功,TCP/IP協定簇逐漸成為UNIX主機間通信的标準網絡協定。Internet的前身ARPANET最初使用NCP(Network Control Protocol)協定,後來由于看到TCP/IP協定簇具有跨平台特性,于是ARPANET的實驗人員對TCP/IP協定簇進行改進後采用,并規定連入ARPANET的計算機必須采用TCP/IP協定簇。
圖31TCP/IP協定簇結構
随着ARPANET逐漸發展成為Internet,TCP/IP協定簇也就成了Internet的标準連接配接協定。
TCP/IP協定簇由四層構成,各層的名稱與所包含的協定如圖31所示,其中的應用層與鍊路層包含的協定衆多,而傳輸層和網絡層包含的協定較少。
3.2鍊路層
鍊路層也稱為資料鍊路層或網絡接口層,該層包括主機用于連接配接網絡的網絡接口卡及其驅動程式,主要處理與傳輸媒介(如雙絞線、光纖、無線電波等)的實體接口細節。由于絕大部分主機使用Ethernet網卡接入網絡,是以,鍊路層所使用的通信協定一般為Ethernet。
鍊路層處理的資料為資料幀,格式如圖32所示,其中,目标MAC(Media Access Control)位址、源MAC位址和類型組成14(6+6+2)位元組的幀頭,後面為資料部分,最後為CRC(Cyclic Redundancy Check)部分。
圖32鍊路層資料幀格式
在圖32中,MAC位址為主機網絡接口卡位址; 類型為來自網絡層的資料類型,IPv4為0x0800,ARP為0x0806,PPPoE為0x8864,802.1Q tag為0x8100,IPv6為0x86DD,MPLS Label為0x8847; 資料部分為來自網絡層的資料,最少為46位元組,最大為1500位元組; CRC為循環備援校驗碼,主要校驗所收到的資料是否有錯。鍊路層資料幀最小為64(6+6+2+46+4)位元組,最大為1518(6+6+2+1500+4)位元組,主要利用網絡接口卡的實體位址即MAC位址進行通信。
主機作為資料發送方時,鍊路層負責将來自本機網絡層的資料封包封裝為資料幀進行發送,資料接收方在收到資料幀後會給資料發送方發送回報資訊,如果資料傳輸有誤,發送方需要重新發送出錯的幀資料; 主機作為資料接收方時,鍊路層負責對接收到的資料幀進行CRC校驗,并給資料發送方發送回報資訊,要求重新發送出錯的幀資料,并将接收到的正确幀資料的目标MAC位址、源MAC位址和CRC部分去掉後,遞交給網絡層處理。
鍊路層通信用MAC位址識别主機,主機間交換資料幀。
代碼31可以擷取本機網卡的MAC位址。
1#代碼31link01.py
2#!/usr/bin/env python3
3# coding: utf-8
4import uuid
. 5node = uuid.uuid1()
6print('type(node)=',type(node))
7print('node=',node)
8hex = node.hex
9print('hex=',hex)
10mac_addr = hex[-12:]
11print('mac_addr=',mac_addr)
代碼31第4行引入uuid,uuid實作UUID(Universally Unique Identifier),即全局唯一辨別符。第5行通過調用uuid的uuid1()函數,得到由MAC位址、目前時間戳和随機數字生成,以對象node表示的UUID值。第8行通過對象node的hex屬性得到包含主機MAC位址的字元串,第10行取字元串的後12位,即為主機的MAC位址。程式運作結果如圖33所示。
type(node)=
node=997041ae-937a-11e7-a2db-000c294be4dd
hex=997041ae937a11e7a2db000c294be4dd
mac_addr=000c294be4dd
圖33代碼31運作結果
Python的第三方子產品psutil可以擷取主機的大量資訊,其中包括主機全部網卡的裝置名稱和MAC位址,在指令行下線上安裝psutil的步驟如下。
sudo apt-get install python3-pip
pip3 install psutil
其中,指令“sudo aptget install python3pip”用于安裝pip工具,pip是Python軟體包管理工具,此處安裝的pip版本号為3; 指令“pip3 install psutil”使用pip工具安裝psutil子產品。
利用psutil擷取主機網卡裝置名稱和MAC位址程式如代碼32所示。
1#代碼32link02.py
2#!/usr/bin/env python3
3# coding: utf-8
4import psutil
5info=psutil.net_if_addrs()
6print('info=',info)
7print('type(info)=',type(info))
8net1=info['eth0']
9print('net1=',net1)
10print('type(net1)=',type(net1))
11packet=net1[2]
12print('packet=',packet)
13print('type(packet)=',type(packet))
14print('mac_addr=',packet.address)
代碼32第4行引入了psutil,調用psutil的net_if_addrs()函數擷取了主機全部的網卡資訊,然後從擷取的資訊中逐漸得到網卡裝置名稱和MAC位址,程式運作結果如圖34所示。
如圖34所示,主機全部網卡的資訊儲存在字典型變量info中,其中包含的網卡裝置名稱分别為eth0和lo; 網卡eth0的資訊儲存在清單變量net1中; 從net1中取得的網卡實體資訊儲存在對象packet中,最後從packet的屬性address中取得網卡的MAC位址。
注: 有些第三方子產品,例如,psutil,安裝後,隻能由系統預設安裝的Python調用,此時,應将系統預設安裝的Python設定為最高優先級,在指令行下執行調用psutil子產品的程式,在Atom內建環境執行這些程式會抛出異常。
info= {'eth0': [snic(family=, address='192.168.3.17', netmask='255.255.255.0', broadcast='192.168.3.255', ptp=None), snic(family=, address='fe80∷d669:5643:a4a5:e4b4%eth0', netmask='ffff:ffff:ffff:ffff∷', broadcast=None, ptp=None), snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)], 'lo': [snic(family=, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None), snic(family=, address='∷1', netmask='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', broadcast=None, ptp=None), snic(family=, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)]}
type(info)=
net1= [snic(family=, address='192.168.3.17', netmask='255.255.255.0', broadcast='192.168.3.255', ptp=None), snic(family=, address='fe80∷d669:5643:a4a5:e4b4%eth0', netmask='ffff:ffff:ffff:ffff∷', broadcast=None, ptp=None), snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]
type(net1)=
packet= snic(family=, address='00:0c:29:4b:e4:dd', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)
type(packet)=
mac_addr= 00:0c:29:4b:e4:dd