計算機網絡基礎知識總結(python實現)

OSI模型(七層)

圖片

TCP/IP 4層模型

圖片

一、鏈路層

  • 概念:鏈路層也稱爲數據鏈路層或網絡接口層,該層包括主機用於鏈接網絡的網絡接口卡及其驅動程序。
  • 功能:主要處理傳輸媒介(如雙絞線、光纖、無線電波等)的物理接口細節。
  • 通信協議:一般爲Ethernet
  • 處理數據:數據幀
  • 兩個重要設備名稱:網橋和交換機。

其中,目標MAC(Media Access Control)地址、源MAC地址和類型組成14(6+6+2)字節的幀頭,後面爲數據部分,最後爲CRC(Cyclic Redundancy Check)部分。

目標MAC地址(6B) 源MAC地址 (6B) 類型 (2B) 數據 (46~1500B) CRC (4B)

MAC地址爲主機網絡接口卡地址;類型爲來自網絡層的數據類型,IPv4爲0x0800,ARP爲0x0806,PPPoE爲0x8864, 802.1Q tag爲0x8100,IPv6爲0x86DD, MPLSLabel爲0x8847;數據部分爲來自網絡層的數據,最少爲46字節,最大爲1500字節;CRC爲循環冗餘校驗碼,主要校驗所收到的數據是否有錯。鏈路層數據幀最小爲64(6+6+2+46+4)字節,最大爲1518(6+6+2+1500+4)字節,主要利用網絡接口卡的物理地址即MAC地址進行通信。

主機作爲數據發送方時,鏈路層負責將來自本機網絡層的數據報文封裝爲數據幀進行發送,數據接收方在收到數據幀後會給數據發送方發送反饋信息,如果數據傳輸有誤,發送方需要重新發送出錯的幀數據;主機作爲數據接收方時,鏈路層負責對接收到的數據幀進行CRC校驗,並給數據發送方發送反饋信息,要求重新發送出錯的幀數據,並將接收到的正確幀數據的目標MAC地址、源MAC地址和CRC部分去掉後,遞交給網絡層處理。鏈路層通信用MAC地址識別主機,主機間交換數據幀。

""" 
@author:oxff 
@github:https://github.com/oxff644 
"""
#獲取本機網卡的MAC地址 
import uuid node =uuid.uuid1()#通過調用uuid的uuid1()函數,得到由MAC地址、當前時間戳和隨機數字生成,以對象node表示的UUID值 
print('type(node) =',type(node)) 
print('node= ',node)
hex =node.hex#通過對象node的hex屬性得到包含主機MAC地址的字符串print('hex=',hex) mac_addr =hex[-12:]#取字符串的後12位,即爲主機的MAC地址 print('mac_addr=',mac_addr)

Python的第三方模塊psutil可以獲取主機的大量信息,其中包括主機全部網卡的設備名稱和MAC地址。舉個例子:

"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用psutil獲取主機網卡設備名稱和MAC地址程序
import psutil
info =psutil.net_if_addrs()#調用psutil的net_if_addrs()函數獲取主機全部的網卡信息
print('info= ',info)
print('type(info)=',type(info))
net1 =info['eth0']
print('net1= ',net1)
packet =net1[2]
print('packet=',packet)
print('type(packet)=',type(packet))
print('mac_addr =',packet.address)#取得網卡的MAC地址

二、網絡層

  • 功能:負責獲取和維護主機的IP地址,給數據報文選擇路由路徑
  • 基本概念:
    1. IP地址:IP地址是其他協議內容的重要組成部分,是主機間進行網際互聯的標識
    2. IPV4:
    * 概念:IPv4是互聯網協議IP(Internet Protocol)的第4版,也是第一個被廣泛使用,構成現今互聯網技術的基礎協議,是TCP/IP協議簇中的核心協議。
    * 基本構成:4字節即32個二進制位表示一個地址,通常用點分十進制法表示,例如202.201.32.9,其中的數字都是十進制的數字,中間用實心圓點分隔。一個IPv4地址分爲網絡地址和主機地址兩部分,其中網絡地址可以描述爲202.201.32.0/24,表示網絡地址部分爲202.201.32.0,長度爲24位,其餘8位爲主機部分。
    * 分類:IPv4地址根據網絡地址與主機地址的不同劃分方式,分爲A類、B類、C類、D類與E類地址,如表圖片
    * 特殊地址

圖片

    *  數據報文格式
首部(20-60B) 數據(0-65516B)

其中:數據報文首部各字段如下圖片

  • VER: IP協議的版本號。值爲二進制0100時爲IPv4,值爲二進制0110時爲IPv6。

  • IHL:數據報文首部長度。數據報文首部長度等於IHL值乘以4,例如,IHL爲二進制0101時,數據報文首部長度爲20(5×4)B;IHL爲二進制1111時,數據報文首部長度爲60(15×4)B。

  • DS:區分服務(Differentiated Services)。DS的前3位表示優先級,優先級低的數據報文在網絡擁塞時會被丟棄;接下來4位分別表示最小時延、最大吞吐量、最高可靠性和最小代價,表示數據報文傳送過程中的期望,該4位中最多有1位爲1,共有5種組合;DS的最後1位未使用。

  • 標識:唯一標識數據報文的16位二進制數值。當數據報文總長度大於1500B時,對數據報文分片時,該值被複制到分好片的數據報文中;同理,來自鏈路層,具有相同標識的多個數據報文會被重新組裝爲一個大的數據報文。

  • 標誌:表示數據報文是否可分片或者是否爲最後一個分片。最高位是預留位,其值爲0。當中間位爲0時,表示該數據報文可以分片;當中間位爲1時,表示該數據報文不可以分片。當最後的位爲1時,表示該數據報文爲分片數據報,且後面還有分片;當最後的位爲0時,表示該數據報文已是最後一個分片。

  • 分片偏移:表示該數據報文在原數據報文中的相對位置。相對位置必須爲8字節的整數倍,也就是分片偏移值乘以8。

  • 生存時間:以數據報文在網絡中的剩餘跳數表示數據報文在網絡中的壽命。數據報文到達某個路由器時,在路由器轉發該數據報文之前,先將生存時間減1,如果生存時間變爲0,則丟棄該數據報文;否則,進行轉發。數據報文的生存時間最大值爲255,表示數據報文在網絡中最多被轉發255次,如果數據報文的生存時間初始值爲1,則該數據報文只能在本地局域網內部傳輸。

  • 協議:指出該數據報文內容所屬的協議。值爲1時,來自ICMP(Internet Control MessageProtocol);值爲2時,來自IGMP(Internet Group Management Protocol);值爲6時,來自TCP(Transmission Control Protocol);值爲17時,來自UDP(User DatagramProtocol);值爲89時,來自OSPF(Open Shortest Path First)協議。

  • 首部校驗和:該數據報文的首部校驗和,未包括數據部分。因爲數據報文每經過一個路由器,首部的一些內容會發生變化,如生存時間,這就需要路由器重新計算首部校驗和,如果包括了數據部分,則會加大計算量。

  • 源IP地址:發送數據的主機的IPv4地址。

  • 目標IP地址:接收數據的主機的IPv4地址。

  • 選項:可選數據。

    3.IPV6:

    • 概念:IPv6是互聯網協議IP(Internet Protocol)的第6版,使用16字節也就是128個二進制位表示1個地址,由於IPv6地址長度4倍於IPv4,因此,IPv4所採用的點分十進制格式表示地址的方式不再適用。
    • 表示方法:
    1. 	冒分十六進制表示法
    2. 	0位壓縮表示法
    3. 	內嵌IPv4地址表示法
    
    • 組成:IPv6數據報文由報文頭部、擴展報文頭部和數據三部分組成,其中報文頭部長度固定爲40字節,擴展報文頭部和數據部分長度可變。
      | 報文頭部(40B) | 擴展報文頭部(長度可變) | 數據(長度可變) |
      |----|----|----|
      4.網絡層協議:
ARP(地址解析協議) 用於將網絡層的IP地址對應到鏈路層的網卡MAC地址
RARP(反向地址解析協議) 用於局域網中的主機向網關的ARP表或者緩存請求IP地址
ICMP(Internet控制報文協議) 用於主機與路由器之間傳遞控制消息,控制消息包括網絡是否通暢、數據是否到達目標主機、路由是否可用等內容
IGMP(Internet組管理協議) 用於管理組播組成員的加入和離開,主機通過IGMP通知組播路由器希望接收或離開某個特定組播組的信息,組播路由器通過IGMP週期性地查詢組播組成員是否處於活動狀態,實現所連網段組成員關係的收集與維護。

Python的第三方模塊psutil可以獲取計算機IP地址及其他網絡配置信息,舉個例子:

"""
@author:oxff
@github:https://github.com/oxff644
"""
#獲取計算機IP地址
import psutil
info =psutil.net_if_addrs()
print('info =',info)
net1 =info['eth0']
net2 =info['lo']
print('net1=',net1,end=' ')
print('net2=',net2,end=' ')
print('net1[0]=',net1[0])
print('net1[1]=',net1[1])
print('IPv4_addr=',net1[0].address)
print('IPv6_addr=',net1[1].address)
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#獲取局域網網關地址
import netifaces
info =netifaces.gateways()
print('info =',info)
print('type(info)=',type(info))
gateway_addr =info['default'][2][0]
print('gateway_addr=',gateway_addr)
#利用python 實現獲取主機收發數據的統計信息
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#獲取局域網網關地址
import psutil
info =psutil.net_io_counters()#調用net_io_counters()函數獲取主機收發數據的統計信息,統計信息存入snetio對象info中
'''
調用net_io_counters()函數時,如果加上參數pernic=True,
即info=psutil.net_io_counters(pernic=True)
將逐個網卡取得數據收發信息並存入字典變量info中。
'''
print('info=',info)
print('type(info)=',type(info))
print('bytes_sent=',info.bytes_sent)#收發數據字節數
print('bytes_recv=',info.bytes_recv)
print('packets_sent=',info.packets_sent)#收發數據報文數
print('packets_recv=',info.packets_recv)
print('errin =',info.errin)#提取出錯的收發報文數
print('errout =',info.errout)
print('dropin=',info.dropin)#提取丟棄的收發數據報文shu
print('dropout=',info.dropout)
  • Socket編程
    • 網絡上的主機間通過IP地址與端口號進行通信,稱爲Socket通信,其實,TCP/IP協議中應用層的HTTP、FTP、DNS等都是通過Socket通信實現的。
    • 在Socket通信中,提供服務的一端稱爲Socket服務端,調用Socket服務的一端稱爲Socket客戶端。Socket服務端首先用自己的IP地址、指定端口號和連接方式創建服務並啓動服務,等待來自客戶端的連接請求;Socket客戶端向服務端發起連接請求,連接請求被服務端接受後,雙方就可以進行通信
    • Socket編程中要實現服務器端同時服務於多個客戶端,需要用到多進程或者多線程技術,Python中的socketserver模塊內置服務器端多任務處理機制,可用多進程或者多線程實現服務器端多任務的同時執行。
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#掃描主機端口
import socket
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)#使用IPv4地址,SOCK_STREAM表示使用TCP連接
s.settimeout(0.5)#用函數settimeout()設置s操作的超時時間,因爲是針對本機操作,速度很快,因此,設置爲0.5s,如果針對遠程主機操作,該值應該設爲一個較大的值,如設置爲3s
ip='192.168.0.103'
for port in range(5000,9000):
    result =s.connect_ex((ip,port))#調用函數connect_ex()連接服務器
    if result==0:#result的值是否爲0,若爲0,說明掃描端口處於打開狀態
        print('port %d is openned!'% port)
s.close()

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用進程池掃描主機端口
from multiprocessing import Pool
import socket
import os
def scan_port(ports):
    s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1)
    for port in range(ports,ports+4096):
        result =s.connect_ex((ip,port))
        if result ==0:#判斷變量result值是否爲0,若爲0表示所測試的端口處於打開狀
            print('I am process %d,port %d is openned!'%(os.getpid(),port))
    s.close()
ip ='192.168.0.103'
p =Pool(16)#用函數Pool()創建進程池,參數16表示進程池中有16個子進程
for k in range(16):
    p.apply_async(scan_port,args=(k*4096,))#以非阻塞方式給進程池中的子進程分配任務,scan_port指明子進程要執行的函數名,args=(k*4096,)以命名參數形式給出元組類型的參數,每個子進程要掃描的開始端口是k*4096
p.close()
p.join()#調用函數join()等待進程池中子進程執行結束
print("all subprocesses had finihed!!")
#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#利用線程池掃描主機端口
import threadpool
import socket
def scan_port(num):
    s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1)
    ports =num*4096
    thread_name ='thread'+str(num)
    for port in range(ports,ports+4096):
        result =s.connect_ex((ip,port))
        if result ==0:
            print('I am %s,port %d is openned!' %(thread_name,port))
    s.close()
ip ='192.168.0.104'
p=threadpool.ThreadPool(16)
num_list =list(range(16))
tasks =threadpool.makeRequests(scan_port,num_list)
for task in tasks:
    p.putRequest(task)
p.wait()
print('All threads had finished!!')
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#探測主機是否在線的實例,實現類似ping命令的功能
import socket
import struct#用於將其他類型數據轉換爲bytes字節流類型
import array#用於將bytes字節流數據轉換爲2B有符號整型數組
import time
def checksum(packet):
    if len(packet) & 1:
        packet =packet+'\0'#判斷報文數據長度是否奇數,若爲奇數,則在報文末尾追加字符“\0”
    words =array.array('h',packet)#將bytes類型的報文數據轉換爲2B有符號的整型數組words,因爲ICMP報文的校驗和爲16位(2B)
    sum =0
    for word in words:
        sum +=(word & 0xffff)#word&0xffff運算保證累加到sum的值爲word的低16位
    sum =(sum>>16)+(sum & 0xffff)#將sum的高16位與低16位相加存入sum中
    sum =sum +(sum>>16)
    return (~sum) & 0xffff#sum進行按位取反後與0xffff進行與運算的結果,該運算保證返回值的高16位爲0,低16位爲sum的反碼計算結果
'''
利用struct模塊的pack()函數構造ICMP報文的首部,
其中,類型值爲8,表示該報文爲探測目標主機是否存在的迴應請求報文,
代碼爲0,校驗和爲0,標識符爲1234,序號爲5
'''
header =struct.pack('bbHHh',8,0,0,1234,5)
data =struct.pack('d',time.time())#構造ICMP報文的數據部分,數據部分值取系統當前的時間
packet =header+data
chkSum =checksum(packet)#以packet爲參數調用函數checksum,計算ICMP報文的校驗和,保存在變量chkSum中
header =struct.pack('bbHHh',8,0,chkSum,1234,5)#以新的校驗和chkSum重新生成ICMP報文的首部
packet =header+data
s =socket.socket(socket.AF_INET,socket.SOCK_RAW,socket.getprotobyname('icmp'))
s.settimeout(3)#設置socket對象s的超時時間爲3s
ip =input('ip adress is:')
for kk in range(4):#利用循環發送探測主機的ICMP數據報文並接收應答報文
    try:
        t1 =time.time()
        s.sendto(packet,(ip,0))
        (r_data,r_addr) =s.recvfrom(1024)
        t2 =time.time()
    except Exception as e:
        print('Error is',e)
        continue
    print('Receive the respond from %s,data is %d bytes,time is %.2f ms'\
          %(r_addr[0],len(r_data),(t2-t1)*1000))
    (h1,h2,h3,h4,h5) =struct.unpack('bbHHh',r_data[20:28])#r_data[20:28]取IP報文序號20~27的字節流,這8字節的字節流爲ICMP報文的首部,r_data[0:20]爲IP報文的首部
    print('type =%d,code =%d,chksum =%u,Id=%u,SN=%d' %(h1,h2,h3,h4,h5))#輸出ICMP報文首部的5個字段值,分別爲類型、代碼、校驗和、標識符和序號。
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
網絡嗅探就是利用SOCK_RAW直接在鏈路層獲取數據報文,
然後對報文進行分析,獲取主機正在進行的網絡通信信息,
例如,通信的源主機、目標主機、所使用的協議等。
網絡嗅探不影響主機的正常通信.
Linux系統中使用PF_PACKET、Windows系統中使用AF_PACKET。
'''
import socket
import struct
import binascii#用於將二進制數據轉換爲ASCII數據
s =socket.socket(socket.PE_PACKET,socket.SOCK_RAW,socket.htons(0x0800))#表示IPv4的編號0x0800由主機順序轉換爲2B的網絡順序,此行語句執行需要root用戶權限
for xx in range(10):
    data =s.recvfrom(2048)
    print('\nFrame number is %d:' % xx)
    packet =data[0]
    frame_leader_b =packet[0:14]
    frame_header_s =struct.unpack("!6s6s2s",frame_leader_b)
    source_MAC_Addr =binascii.hexlify(frame_header_s[0])
    dest_MAC_Addr =binascii.hexlify(frame_header_s[1])
    proto_type =binascii.hexlify(frame_header_s[2])
    print('Source MAC address is',source_MAC_Addr)
    print('Destination MAC address is',dest_MAC_Addr)
    print('Protocol type is ',proto_type)
    ip_header_b =packet[14:34]
    ip_header_s =struct.unpack('!12s4s4s',ip_header_b)
    print("Protocol is:",ip_header_s[0][9:10])
    print("Source IP address is: "+socket.inet_ntoa(ip_header_s[2]))
    print("Destination IP address is: "+socket.inet_ntoa(ip_header_s[2]))
s.close()

三、傳輸層

  • 功能:主要爲網絡中的兩臺主機提供端到端的數據傳輸服務,作爲數據發送方,傳輸層將來自應用層的數據進行分割並加上傳輸層報文頭部後組裝成傳輸層數據報文,遞交給網絡層處理;作爲數據接收方,傳輸層將來自網絡層的數據去掉傳輸層報文頭部並對數據進行組裝,然後將數據遞交給應用層。
    

    網絡中一臺主機向另外一臺主機發送數據的過程

圖片

  • 傳輸層協議
    
協議名稱 特點 功能 應用場景
TCP 面向連接,可靠 提供面向連接、可靠的數據傳輸服務
UDP 無連接,不對傳輸的數據進行可靠性保證,資源消耗小、處理速度快,實現簡單 提供的服務沒有連接、只能提供盡力而爲的數據傳輸服務, 適合於一次傳輸少量數據和傳輸中偶爾出現錯誤對結果影響不大的服務(例如,音頻、視頻等服務),但對於需要傳輸較多數據,且數據傳輸出現錯誤需要重傳的服務並不適合,使用UDP協議包括:TFTP(簡單文件傳輸協議)、SNMP(簡單網絡管理協議)、DNS(域名解析協議)、NFS、BOOTP

TCP協議的三次握手和四次揮手:

圖片

:seq:"sequance"序列號;ack:"acknowledge"確認號;SYN:"synchronize"請求同步標誌;;ACK:“acknowledge"確認標誌”;FIN:"Finally"結束標誌。

  • TCP連接建立過程:首先Client端發送連接請求報文,Server段接受連接後回覆ACK報文,併爲這次連接分配資源。Client端接收到ACK報文後也向Server段發生ACK報文,並分配資源,這樣TCP連接就建立了。

  • TCP連接斷開過程:假設Client端發起中斷連接請求,也就是發送FIN報文。Server端接到FIN報文後,意思是說"我Client端沒有數據要發給你了",但是如果你還有數據沒有發送完成,則不必急着關閉Socket,可以繼續發送數據。所以你先發送ACK,“告訴Client端,你的請求我收到了,但是我還沒準備好,請繼續你等我的消息”。這個時候Client端就進入FIN_WAIT狀態,繼續等待Server端的FIN報文。當Server端確定數據已發送完成,則向Client端發送FIN報文,“告訴Client端,好了,我這邊數據發完了,準備好關閉連接了”。Client端收到FIN報文後,"就知道可以關閉連接了,但是他還是不相信網絡,怕Server端不知道要關閉,所以發送ACK後進入TIME_WAIT狀態,如果Server端沒有收到ACK則可以重傳。“,Server端收到ACK後,“就知道可以斷開連接了”。Client端等待了2MSL後依然沒有收到回覆,則證明Server端已正常關閉,那好,我Client端也可以關閉連接了。Ok,TCP連接就這樣關閉了!

    爲什麼要三次揮手?

    在只有兩次“握手”的情形下,假設Client想跟Server建立連接,但是卻因爲中途連接請求的數據報丟失了,故Client端不得不重新發送一遍;這個時候Server端僅收到一個連接請求,因此可以正常的建立連接。但是,有時候Client端重新發送請求不是因爲數據報丟失了,而是有可能數據傳輸過程因爲網絡併發量很大在某結點被阻塞了,這種情形下Server端將先後收到2次請求,並持續等待兩個Client請求向他發送數據…問題就在這裏,Cient端實際上只有一次請求,而Server端卻有2個響應,極端的情況可能由於Client端多次重新發送請求數據而導致Server端最後建立了N多個響應在等待,因而造成極大的資源浪費!所以,“三次握手”很有必要!

    爲什麼要四次揮手?

    試想一下,假如現在你是客戶端你想斷開跟Server的所有連接該怎麼做?第一步,你自己先停止向Server端發送數據,並等待Server的回覆。但事情還沒有完,雖然你自身不往Server發送數據了,但是因爲你們之前已經建立好平等的連接了,所以此時他也有主動權向你發送數據;故Server端還得終止主動向你發送數據,並等待你的確認。其實,說白了就是保證雙方的一個合約的完整執行!

    使用TCP的協議:FTP(文件傳輸協議)、Telnet(遠程登錄協議)、SMTP(簡單郵件傳輸協議)、POP3(和SMTP相對,用於接收郵件)、HTTP協議等。

1.多進程TCP實例

服務端代碼:

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
多進程TCP實例(服務端)
要實現socketserver的多進程TCP編程
需要調用socketserver的ForkingTCPServer()函數
'''
import socketserver
def factorial(n):
    s=1
    for x in range(2,n+1):
        s =s*x
    return s
class Factorial_server(socketserver.StreamRequestHandler):
    def handle(self):
        conn =self.request
        try:
            data_b =conn.recv(1024)
            data_s =data_b.decode('utf-8')
            data =int(data_s)
            if data >1:
                fact =factorial(data)
            else:
                fact =1
            fact_s =str(fact)
            fact_b =fact_s.encode('utf-8')
            conn.send(fact_b)
        print('factorial(%d) =%s,from %s'% (data,fact_s,self.client_address[0]))
        except Exception as e:
            print('Error is ',e)
ip ='192.168.0.103'
server =socketserver.ForkingTCPServer((ip,8899),Factorial_server)
print('wait for TCO connecting...')

server.serve_forever()

客戶端代碼:
#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#客戶端代碼
import socket
ip ='192.168.0.103'
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,8899))
xx_s =input('Enter a number:')
xx_b =xx_s.encode('utf-8')
s.send(xx_b)
result_b =s.recv(1024)
result_s =result_b.decode('utf-8')
print('Factorial(%s) =%s' % (xx_s,result_s))
s.close()

服務器與客戶端之間傳輸數值時,先將數值轉換爲字符串,再將字符串轉換爲bytes後傳輸,實現不同長度數值的傳輸。

2.多進程UDP實例

服務端程序

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
'''
UDP服務器程序
要實現socketserver的多進程UDP編程,
需要調用socketserver的Forking-UDPServer()函數
'''
import socketserver
def factorial(n):
    s=1
    for x in range(2,n+1):
        s=s*x
    return s
class Factorial_server(socketserver.DatagramRequestHandler):
    def handle(self):
        try:
            (data_b,s) =self.request
            data_s =data_b.decode('utf-8')
            data =int(data_s)
            if data>1:
                fact =factorial(data)
            else:
                fact =1
            fact_s =str(fact)
            fact_b =fact_s.encode('utf-8')
            s.sendto(fact_b,self.client_address)
            print('factorial(%d)= %s,from %s' % (data,fact_s,self.client_address[0]))
        except Exception as e:
            print('Error is ',e)
ip ='192.168.0.103'
server =socketserver.ForkingUDPServer((ip,8988),Factorial_server)
print('Bins UDP on 8988....')
server.serve_forever()

客戶端程序

#!/usr/bin/env python3
#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
import socket
ip ='192.168.0.103'
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
xx_s =input('enter a number:')
xx_b =xx_s.encode('utf-8')
s.sendto(xx_b,(ip,8988))
while True:
    (result_b,s_addr) =s.recvfrom(1024)
    if s_addr[0] ==ip:
        result_s =result_b.decode('utf-8')
        print('Factorial(%s)= %s' %(xx_s,result_s))
        break
s.close()

四、應用層

  • 常用協議
  1. HTTP
  • HTTP(HyperText Transfer Protocol)是互聯網上應用最爲廣泛的一種網絡協議,用於傳輸HTML(HyperTextMarkup Language)數據,而HTML是互聯網中主機之間進行交互的標準語言。
  • HTTP規定通信雙方分別爲客戶端和服務器端,雙方以請求/應答方式工作,即客戶端向服務器端發起請求,服務器端對來自客戶端的請求進行應答,雙方交互的數據使用可靠傳輸協議TCP進行傳輸。
  • HTTP的報文分爲請求報文和應答報文
  • HTTP請求報文和HTTP應答報文兩者都是HTML文本,分成多行,用CRLF表示換行,行內的字段長度沒有限制,用空格作爲分隔fu

利用Python3的urllib所包含的模塊request可以設置HTTP請求報文首部,獲取指定服務器端的HTTP響應報文,代碼如下:

#coding=utf-8
"""
@author:oxff
@github:https://github.com/oxff644
"""
#獲取指定服務器端的HTTP響應報文
from urllib import request
url ='http://www.baidu.com'
header ={
    'Accept':'text/html',
    'Connection':'keep-alive'
}
req =request.Request(url,headers=header)
response =request.urlopen(req)
print('Status code=',response.getcode())
print('url=',response.geturl())
print('info=',response.info())

2.HTTPS

  • HTTPS使用SSL(Secure Sockets Layer)或者TLS(Transport Layer Security)對HTTP數據報文進行加密後遞交給傳輸層的TCP進行傳輸,使用端口默認爲443
    
  • 主機間使用HTTPS通信時,會同時使用到非對稱加密算法即公/私鑰加密算法、對稱加密算法、HASH算法等,其中,非對稱加密算法用於加密密碼,對稱加密算法用於加密要發送的消息和HASH值,HASH算法用於計算要發送消息的HASH值
    

3.FTP

  • FTP(File Transfer Protocol,文件傳輸協議)用於Internet上主機間文件的共享與傳輸。
    
  • FTP服務實現包括服務器端和客戶端。服務器端是提供文件存儲空間的計算機,經常被稱爲FTP服務器;客戶端是通過Internet以FTP訪問服務器的主機。通過Internet,從FTP服務器端複製文件至客戶端,稱爲“下載(download)”;將客戶端的文件複製到FTP服務器上,則稱爲“上傳(upload)”。
    
  • FTP服務器端和客戶端的連接需要使用兩個獨立的TCP連接:一個是命令鏈路,用來傳送命令和狀態數據;另一個是數據鏈路,用來傳輸數據。
    
  • FTP協議有兩種工作方式:PORT和PASV,即主動式和被動式工作方式。
    

    1.PORT(主動)工作方式的過程是:客戶端向服務器的FTP端口(默認是21)發送連接請求,服務器接受連接,建立一條命令鏈路。當需要傳送數據時,客戶端在命令鏈路上用PORT命令告訴服務器,“我打開了XX端口,你來連接我”,於是服務器利用20端口向客戶端的XX端口發送連接請求,建立一條數據鏈路來傳送數據。

    2.PASV(被動)工作方式的過程是:客戶端向服務器的FTP端口(默認是21)發送連接請求,服務器接受連接,建立一條命令鏈路。當需要傳送數據時,服務器在命令鏈路上用PASV命令告訴客戶端,“我打開了XX端口,你來連接我”,於是客戶端向服務器的XX端口發送連接請求,建立一條數據鏈路來傳送數據。(實際應用中經常使用PASV工作方式)

4.DNS

  • DNS(Domain Name System)能夠提供互聯網域名解析服務,是互聯網的一項核心服務
  • DNS數據報文格式DNS數據報文一般使用UDP協議傳輸,常用端口號爲53

這裏舉例說明域名解析過程。

例如,某用戶單擊鏈接www.linux.com,則其本地DNS服務器開始搜索自己的DNS數據庫信息,如果沒有搜索到,就轉到上級DNS服務器,若上級DNS服務器也沒有該域名的記錄,則繼續轉上級DNS服務器,直到根DNS服務器。根DNS服務器在其DNS數據庫裏查找COM頂級域,然後它用NS記錄回覆該用戶DNS服務器,指示可以從linux.com的DNS服務器ns.linux.com處查詢到www.linux.com的信息。於是,經過DNS服務器ns.linux.com的轉換,該用戶得到了www.linux.com的對應IP地址,並且其DNS服務器緩存了該NS記錄。下次如果有用戶再需要解析該域名時,相關信息在本地即可獲得。

5.SMTP

  • SMTP(Simple Mail Transfer Protocol)即簡單郵件傳輸協議,用於將郵件從源地址傳送到目的地址。
    
  • SMTP利用傳輸層的TCP協議傳輸信件數據,使用端口一般爲25。
    
  • 利用SMTP通信的雙方通過命令和應答的方式交換文本信息,文本信息以CRLF作爲結束標誌,類似於FTP的命令鏈路,因此,沒有定義特定的報文格式
    

6.POP3

  • POP3(Post Office Protocol 3)即郵局協議版本3,用於收信方從自己的郵件服務器上接收其他用戶發給自己的郵件,是常用的郵件接收協議
    
  • POP3利用傳輸層的TCP協議傳輸數據,使用端口一般爲110
    
  • POP3屬於離線式協議,即收信方登錄到郵件服務器後,會一次性將所有郵件下載到本地計算機上,服務器上的郵件同時被刪除。
    
  • 與SMTP類似,使用POP3通信的雙方通過命令和應答方式交換文本信息,文本信息以CRLF作爲結束標誌,因此,沒有定義特定的報文格式
    
  • IMAP(Internet Mail Access Protocol)即互聯網郵件操作協議,屬於在線式協議,允許用戶在線操作郵件,方便用戶隨時隨地利用不同終端設備處理郵件,彌補了POP3的不足,是目前最常用的郵件接收協議
    
  • IMAP與POP3主要功能比較
    

圖片

7. DHCP

  • DHCP(Dynamic Host Configuration Protocol)即動態主機配置協議,是一個簡化主機IP地址分配管理的協議。用戶可以利用DHCP服務器管理動態的IP地址分配及其他相關的環境配置工作(如DNS、WINS、Gateway的設置)
    

8.NAT協議

  • NAT網絡地址轉換(Network Address Translation)屬接入廣域網(WAN)技術,是一種將私有(保留)地址轉化爲合法IP地址的轉換技術,它被廣泛應用於各種類型Internet接入方式和各種類型的網絡中。原因很簡單,NAT不僅完美地解決了lP地址不足的問題,而且還能夠有效地避免來自網絡外部的攻擊,隱藏並保護網絡內部的計算機。
    

五、舉例

**  **在瀏覽器中輸入 www.baidu.com  後執行的全部過程

現在假設如果我們在客戶端(客戶端)瀏覽器中輸入http://www.baidu.com,而baidu.com爲要訪問的服務器(服務器),下面詳細分析客戶端爲了訪問服務器而執行的一系列關於協議的操作:

1)客戶端瀏覽器通過DNS解析到www.baidu.com的IP地址220.181.27.48,通過這個IP地址找到客戶端到服務器的路徑。客戶端瀏覽器發起一個HTTP會話到220.161.27.48,然後通過TCP進行封裝數據包,輸入到網絡層。

2)在客戶端的傳輸層,把HTTP會話請求分成報文段,添加源和目的端口,如服務器使用80端口監聽客戶端的請求,客戶端由系統隨機選擇一個端口如5000,與服務器進行交換,服務器把相應的請求返回給客戶端的5000端口。然後使用IP層的IP地址查找目的端。

3)客戶端的網絡層不用關係應用層或者傳輸層的東西,主要做的是通過查找路由表確定如何到達服務器,期間可能經過多個路由器,這些都是由路由器來完成的工作,不作過多的描述,無非就是通過查找路由表決定通過那個路徑到達服務器。

4)客戶端的鏈路層,包通過鏈路層發送到路由器,通過鄰居協議查找給定IP地址的MAC地址,然後發送ARP請求查找目的地址,如果得到迴應後就可以使用ARP的請求應答交換的IP數據包現在就可以傳輸了,然後發送IP數據包到達服務器的地址。


參考:

1.《python網絡編程(linux)》趙宏,包廣斌,馬棟林編著.—北京:清華大學出版社,2018(大數據與人工智能技術叢書)

2.https://www.cnblogs.com/maybe2030/p/4781555.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章