1. 程式人生 > >Python之——網路程式設計

Python之——網路程式設計

轉載請註明出處:https://blog.csdn.net/l1028386804/article/details/83046191

一、C/S架構

客戶端/服務端架構

二、OSI七層架構

七層模型,亦稱OSI(Open System Interconnection)參考模型,是參考模型是國際標準化組織(ISO)制定的一個用於計算機或通訊系統間互聯的標準體系。它是一個七層的、抽象的模型體,不僅包括一系列抽象的術語或概念,也包括具體的協議。
分層:

應用層 (Application):

網路服務與終端使用者的一個介面。
協議有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP

表示層(Presentation Layer):

資料的表示、安全、壓縮。(在五層模型裡面已經合併到了應用層)
格式有,JPEG、ASCll、DECOIC、加密格式等

會話層(Session Layer):

建立、管理、終止會話。(在五層模型裡面已經合併到了應用層)
對應主機程序,指本地主機與遠端主機正在進行的會話

傳輸層 (Transport):

定義傳輸資料的協議埠號,以及流控和差錯校驗。
協議有:TCP UDP,資料包一旦離開網絡卡即進入網路傳輸層

網路層 (Network):

進行邏輯地址定址,實現不同網路之間的路徑選擇。
協議有:ICMP IGMP IP(IPV4 IPV6) ARP RARP

資料鏈路層 (Link):

建立邏輯連線、進行硬體地址定址、差錯校驗等功能。(由底層網路定義協議)
將位元組合成位元組進而組合成幀,用MAC地址訪問介質,錯誤發現但不能糾正。

物理層(Physical Layer):

建立、維護、斷開物理連線。(由底層網路定義協議)

三、TCP的三段握手和四次斷開

相對於SOCKET開發者,TCP建立過程和連結折除過程是由TCP/IP協議棧自動建立的.因此開發者並不需要控制這個過程.但是對於理解TCP底層運作機制,相當有幫助.

TCP三次握手

所謂三次握手(Three-way Handshake),是指建立一個TCP連線時,需要客戶端和伺服器總共傳送3個包。
三次握手的目的是連線伺服器指定埠,建立TCP連線,並同步連線雙方的序列號和確認號並交換 TCP 視窗大小資訊.在socket程式設計中,客戶端執行connect()時。將觸發三次握手。

第一次握手:

客戶端傳送一個TCP的SYN標誌位置1的包指明客戶打算連線的伺服器的埠,以及初始序號X,儲存在包頭的序列號(Sequence Number)欄位裡。

第二次握手:

伺服器發回確認包(ACK)應答。即SYN標誌位和ACK標誌位均為1同時,將確認序號(Acknowledgement Number)設定為客戶的I S N加1以.即X+1。

第三次握手.

客戶端再次傳送確認包(ACK) SYN標誌位為0,ACK標誌位為1.並且把伺服器發來ACK的序號欄位+1,放在確定欄位中傳送給對方.並且在資料段放寫ISN的+1

TCP四次斷開

TCP的連線的拆除需要傳送四個包,因此稱為四次揮手(four-way handshake)。客戶端或伺服器均可主動發起揮手動作,在socket程式設計中,任何一方執行close()操作即可產生揮手操作。

第一次揮手

Client傳送一個FIN,用來關閉Client到Server的資料傳送,Client進入FIN_WAIT_1狀態。

第二次揮手

Server收到FIN後,傳送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。

第三次揮手

Server傳送一個FIN,用來關閉Server到Client的資料傳送,Server進入LAST_ACK狀態。

第四次揮手

Client收到FIN後,Client進入TIME_WAIT狀態,接著傳送一個ACK給Server,確認序號為收到序號+1,Server進入CLOSED狀態,完成四次揮手。

四、socket

socket通常也稱作"套接字",用於描述IP地址和埠,是一個通訊鏈的控制代碼,應用程式通常通過"套接字"向網路發出請求或者應答網路請求。套接字能唯一表示出網際網路上一臺主機上的一個應用程式
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆檔案”,對於檔案用【開啟】【讀寫】【關閉】模式來操作。socket就是該模式的一個實現,socket即是一種特殊的檔案,一些socket函式就是對其進行的操作(讀/寫IO、開啟、關閉)

socket和file的區別:

file模組是針對某個指定檔案進行【開啟】【讀寫】【關閉】
socket模組是針對 伺服器端 和 客戶端Socket 進行【開啟】【讀寫】【關閉】

TCP程式設計:

服務端:

# -*- coding:UTF-8 -*-
'''
 @author liuyazhuang
 @date 2018/10/14 11:58
 @description TCP服務端
 @version 1.0.0
'''
import socket
import threading
import time

def dealClient(sock, addr):
    #第四步:接收傳來的資料,併發送給對方資料
    print 'Accept new connection from %s:%s...' % addr
    sock.send(b'Hello, I am server!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode('utf-8') == 'exit':
            break
        print '-->>%s!' % data.decode('utf-8')
        sock.send('Loop Msg: %s!' % data.decode('utf-8').encode('utf-8'))
    #第五步:關閉socket
    sock.close()
    print 'Connection from %s:%s closed.' % addr

if __name__ == '__main__':
    #第一步:建立一個基於IPV4和TCP協議的Socket
    #Socket繫結的IP(127.0.0.1為本機IP)與埠
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('127.0.0.1', 9999))
    #第二步:監聽連線
    s.listen(5)
    print 'waiting for connection...'
    while True:
        #第三步:接收一個新連線
        sock, addr = s.accept()
        #建立新執行緒來處理TCP連線
        t = threading.Thread(target=dealClient, args=(sock, addr))
        #啟動執行緒
        t.start()

客戶端:

# -*- coding:UTF-8 -*-
'''
 @author liuyazhuang
 @date 2018/10/14 12:14
 @description  TCP客戶端
 @version 1.0.0
'''
import socket

#初始化socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#連線目標的IP和埠
s.connect(('127.0.0.1', 9999))
#接收訊息
print '--->>' + s.recv(1024).decode('utf-8')
#傳送訊息
s.send(b'Hello, I am a client')
print '--->>' + s.recv(1024).decode('utf-8')
s.send(b'exit')
#關閉socket
s.close()

UDP程式設計:

服務端:

# -*- coding:UTF-8 -*-
'''
 @author liuyazhuang
 @date 2018/10/14 12:22
 @description UDP服務端
 @version 1.0.0
'''
import socket

#建立Socket,繫結指定的IP和埠
#SOCK_DGRAM指定了這個Socket型別是UDP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 9999))
print 'Bind UDP on 9999...'
while True:
    data, addr = s.recvfrom(1024)
    print 'Received from %s:%s' % addr
    s.sendto(b'Hello, %s!' % data, addr)

客戶端:

# -*- coding:UTF-8 -*-
'''
 @author liuyazhuang
 @date 2018/10/14 12:25
 @description UDP客戶端
 @version 1.0.0
'''
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Hello', b'World']:
    #傳送資料
    s.sendto(data, ('127.0.0.1', 9999))
    #接收資料
    print s.recv(1024).decode('utf-8')
s.close()

Socket引數使用

## socket引數使用
引數一:地址簇

  socket.AF_INET IPv4(預設)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能夠用於單一的Unix系統程序間通訊

引數二:型別

  socket.SOCK_STREAM  流式socket , for TCP (預設)
  socket.SOCK_DGRAM   資料報式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網路報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由使用者構造IP頭。
  socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付資料報但不保證順序。SOCK_RAM用來提供對原始協議的低階訪問,在需要執行某些特殊操作時使用,如傳送ICMP報文。SOCK_RAM通常僅限於高階使用者或管理員執行的程式使用。
  socket.SOCK_SEQPACKET 可靠的連續資料包服務

引數三:協議

  0  (預設)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

其他引數:sk是自定義的變數,s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

服務端套接字函式
s.bind()    繫結(主機,埠號)到套接字
s.listen()  開始TCP監聽
s.accept()  被動接受TCP客戶的連線,(阻塞式)等待連線的到來

客戶端套接字函式
s.connect()     主動初始化TCP伺服器連線
s.connect_ex()  connect()函式的擴充套件版本,出錯時返回出錯碼,而不是丟擲異常

公共用途的套接字函式
s.recv()            接收TCP資料
s.send()            傳送TCP資料(send在待發送資料量大於己端快取區剩餘空間時,資料丟失,不會發完)
s.sendall()         傳送完整的TCP資料(本質就是迴圈呼叫send,sendall在待發送資料量大於己端快取區剩餘空間時,資料不丟失,迴圈呼叫send直到發完)
s.recvfrom()        接收UDP資料
s.sendto()          傳送UDP資料
s.getpeername()     連線到當前套接字的遠端的地址
s.getsockname()     當前套接字的地址
s.getsockopt()      返回指定套接字的引數
s.setsockopt()      設定指定套接字的引數
s.close()           關閉套接字

面向鎖的套接字方法
s.setblocking()     設定套接字的阻塞與非阻塞模式
s.settimeout()      設定阻塞套接字操作的超時時間
s.gettimeout()      得到阻塞套接字操作的超時時間

面向檔案的套接字的函式
s.fileno()          套接字的檔案描述符
s.makefile()        建立一個與該套接字相關的檔案