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() 建立一個與該套接字相關的檔案