網絡體系結構就是使用這些用不一樣媒介鏈接起來的不一樣設備和網絡系統在不一樣的應用環境下實現互操做性,並知足各類業務需求的一種粘合劑。網絡體系結構解決互質性問題彩是分層方法。python
1.網絡(OSI)的7層模型:shell
應用層--->爲應用程序提供網絡通訊服務編程
表示層--->數據表示windows
會話層--->主機間通訊(兩個應用進程間)服務器
傳輸層--->端到端的鏈接,隔離網絡的上下層協議,使得網絡應用與下層協議無關網絡
網絡層--->尋找最優路徑,轉發數據包數據結構
數據鏈路層--->無差錯的鏈路鏈接併發
物理層--->二進制傳輸異步
2.端口socket
是一種抽象的軟件結構,包括一些數據結構和I/O緩衝區。與協議有關。
3.套接字存在於通訊區域中。
通訊區域也叫地址族,它是一個抽象的概念,主要用於將經過套接字通訊的進程的共有特性綜合在一塊兒。
爲保證數據的正確性,在網絡協議中須要制定網絡字節順序,採用統一的網絡字節順序。
網絡通訊三要素:
IP地址:用於表示主機(IP地址 = 網絡ID+主機ID)
端口號:用於標識進程的邏輯端口
傳輸協議:TCP UDP
網絡通訊過程就是一個不斷封裝和解析的過程
Socket是鏈接應用程序與網絡驅動程序的橋樑,Socket在應用程序中建立,經過綁定操做與驅動程序創建關係。
套接字是爲特定網絡協議(例如TCP/IP,ICMP/IP,UDP/IP等)套件對上的網絡應用程序提供者提供當前可移植標準的對象。它們容許程序接受並進行鏈接,如發送和接受數據。爲了創建通訊通道,網絡通訊的每一個端點擁有一個套接字對象極爲重要。
套接字爲BSD UNIX系統核心的一部分,並且他們也被許多其餘相似UNIX的操做系統包括Linux所採納。許多非BSD UNIX系統(如ms-dos,windows,os/2,mac os及大部分主機環境)都以庫形式提供對套接字的支持。
三種最流行的套接字類型是:stream,datagram和raw。stream和datagram套接字能夠直接與TCP協議進行接口,而raw套接字則接口到IP協議。但套接字並不限於TCP/IP。
套接字模塊是一個很是簡單的基於對象的接口,它提供對低層BSD套接字樣式網絡的訪問。使用該模塊能夠實現客戶機和服務器套接字。要在python 中創建具備TCP和流套接字的簡單服務器,須要使用socket模塊。利用該模塊包含的函數和類定義,可生成經過網絡通訊的程序。
SOCKET內建方法
函數 | 描述 |
服務器端套接字函數 | |
s.bind() | 綁定地址(主機,端口號對)到套接字 s.bind(address) 將套接字綁定到地址。 address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。 |
s.listen() | 開始TCP 監聽 開始監聽傳入鏈接。添加參數是指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。 若是參數等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5 |
s.accept() | 被動接受TCP 客戶的鏈接,(阻塞式)等待鏈接的到來 接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。 接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來 |
客戶端套接字函數 | |
s.connect() | 主動初始化TCP 服務器鏈接,參數爲地址(address) 鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。 |
s.connect_ex() | connect()函數的擴展版本,出錯時返回出錯碼,而不是拋異常 同上,只不過會有返回值,鏈接成功時返回 0 ,鏈接失敗時候返回編碼,例如:10061 |
公共用途的套接字函數 | |
s.recv() | 接收TCP 數據 sk.recv(bufsize[,flag]) 接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。 |
s.send() | 發送TCP 數據 將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。 |
s.sendall() | 完整發送TCP 數據 將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。 內部經過遞歸調用send,將全部內容發送出去。 |
s.recvfrom() | 接收UDP 數據 與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。 |
s.sendto() | 發送UDP 數據 將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。 |
s.getpeername() | 鏈接到當前套接字的遠端的地址 返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。 |
s.getsockname() | 當前套接字的地址 返回套接字本身的地址。一般是一個元組(ipaddr,port) |
s.getsockopt() | 返回指定套接字的參數 |
s.setsockopt() | 設置指定套接字的參數 |
s.close() | 關閉套接字 |
面向模塊的套接字函數 | |
s.setblocking() | 設置套接字的阻塞與非阻塞模式 是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。 |
s.settimeout()a | 設置阻塞套接字操做的超時時間 設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如 client 鏈接最多等待5s ) |
s.gettimeout()a | 獲得阻塞套接字操做的超時時間 |
面向文件的套接字的函數 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 建立一個與該套接字關連的文件 |
創建服務器鏈接須要六個步驟:
1.建立socket對象。調用socket構造函數。
socket=socket.socket(familly,type)
family的值能夠是AF_UNIX(Unix域,用於同一臺機器上的進程間通信),也能夠是AF_INET(對於IPV4協議的TCP和 UDP),至於type參數,SOCK_STREAM(流套接字)或者 SOCK_DGRAM(數據報文套接字),SOCK_RAW(raw套接字)。
2.則是將socket綁定(指派)到指定地址上,socket.bind(address)
address必須是一個雙元素元組,((host,port)),主機名或者ip地址+端口號。若是端口號正在被使用或者保留,或者主機名或ip地址錯誤,則引起socke.error異常。
3.綁定後,必須準備好套接字,以便接受鏈接請求。
socket.listen(backlog)
backlog指定了最多鏈接數,至少爲1,接到鏈接請求後,這些請求必須排隊,若是隊列已滿,則拒絕請求。
4.服務器套接字經過socket的accept方法等待客戶請求一個鏈接:
connection,address=socket.accept()
調用accept方法時,socket會進入'waiting'(或阻塞)狀態。客戶請求鏈接時,方法創建鏈接並返回服務器。accept方法返回 一個含有倆個元素的元組,形如(connection,address)。第一個元素(connection)是新的socket對象,服務器經過它與客 戶通訊;第二個元素(address)是客戶的internet地址。
5. 處理階段,服務器和客戶經過send和recv方法通訊(傳輸數據)。服務器調用send,並採用字符串形式向客戶發送信息。send方法 返回已發送的字符個數。服務器使用recv方法從客戶接受信息。調用recv時,必須指定一個整數來控制本次調用所接受的最大數據量。recv方法在接受 數據時會進入'blocket'狀態,最後返回一個字符串,用它來表示收到的數據。若是發送的量超過recv所容許,數據會被截斷。多餘的數據將緩衝於接 受端。之後調用recv時,多餘的數據會從緩衝區刪除。
6. 傳輸結束,服務器調用socket的close方法以關閉鏈接。
創建一個簡單客戶鏈接則須要4個步驟。
第1步,建立一個socket以鏈接服務器 socket=socket.socket(family,type)
第2步,使用socket的connect方法鏈接服務器 socket.connect((host,port))
第3步,客戶和服務器經過send和recv方法通訊。
第4步,結束後,客戶經過調用socket的close方法來關閉鏈接。
如下案例都是以TCP方式鏈接
import socket sk = socket.socket() address = ('127.0.0.1',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) inp = input(">>>>:") conn.send(bytes(inp,"utf8")) # conn, addr = sk.accept() # while True: # date = conn.recv(1024) # if not date: # conn, addr = sk.accept() # continue # print(str(date, "utf8")) # inp = input(">>>>:") # conn.send(bytes(inp,"utf8")) # conn.close() conn.close()
import socket sk = socket.socket() address = ("127.0.0.1",8000) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) date = sk.recv(1024) print(str(date,"utf8")) sk.close()
簡單的模擬qq對話(socket模塊)
import socket,subprocess sk = socket.socket() address = ('0.0.0.0',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) obj = subprocess.Popen(str(date, "utf8"),shell=True,stdout=subprocess.PIPE) cmd_result = obj.stdout.read() result_len = str(len(cmd_result)) print(result_len) conn.send(bytes(result_len,"utf8")) conn.send(cmd_result) conn.close()
import socket sk = socket.socket() address = ("127.0.0.1",8000) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) result_len = int(str(sk.recv(1024),"utf8")) print(result_len) date = bytes() while len(date) != result_len: da = sk.recv(1024) date+=da print(str(date,"gbk")) sk.close()
簡單的FTP上傳圖片
import subprocess import socket import os sk=socket.socket() print(sk) address=('127.0.0.1',8000) sk.bind(address) sk.listen(3) print('waiting......') BASE_DIR=os.path.dirname(os.path.abspath(__file__)) while 1: conn, addr = sk.accept() while 1: data=conn.recv(1024) cmd,filename,filesize=str(data,'utf8').split('|') path=os.path.join(BASE_DIR,'tupian',filename) filesize=int(filesize) f=open(path,'ab') has_receive=0 while has_receive!=filesize: data=conn.recv(1024) f.write(data) has_receive+=len(data) f.close()
import socket import os sk=socket.socket() address=('127.0.0.1',8000) sk.connect(address) BASE_DIR=os.path.dirname(os.path.abspath(__file__)) while True: inp=input('>>>').strip()# post|11.png cmd,path=inp.split('|') path=os.path.join(BASE_DIR,path) filename=os.path.basename(path) file_size=os.stat(path).st_size file_info='post|%s|%s'%(filename,file_size) sk.sendall(bytes(file_info,'utf8')) f=open(path,'rb') has_sent=0 while has_sent!=file_size: data=f.read(1024) sk.sendall(data) has_sent+=len(data) f.close() print('上傳成功')
socketserver是標準庫中一個高級別的模塊,用於簡化網絡客戶與服務器的實現。模塊中,已經實現了一些可供使用的類。
SocketServer模塊中的類主要有如下幾個:
一、BaseServer 包含服務器的核心功能與混合類(mix-in)的鉤子功能。這個類主要用於派生,不要直接生成這個類的類對象,能夠考慮使用TCPServer和UDPServer類。
二、TCPServer 基本的網絡同步TCP服務器
三、UDPServer 基本的網絡同步UDP服務器
四、ForkingMixIn 實現了核心的進程化功能,用於與服務器類進行混合(mix-in),以提供一些異步特性。不要直接生成這個類的對象。
五、ThreadingMixIn 實現了核心的線程化功能,用於與服務器類進行混合(mix-in),以提供一些異步特性。不要直接生成這個類的對象。
六、ForkingTCPServer ForkingMixIn與TCPServer的組合
七、ForkingUDPServer ForkingMixIn與UDPServer的組合
八、BaseRequestHandler
九、StreamRequestHandler TCP請求處理類的一個實現
十、DataStreamRequestHandler UDP請求處理類的一個實現
1、處理程序
使用本模塊時,必須定義一個繼承於基類(父類)BaseRequestHandler的處理程序類。BaseRequestHandler類的實例h能夠實現如下方法:
一、h.handle() 調用該方法執行實際的請求操做。調用該函數能夠不帶任何參數,可是幾個實例變量包含有用的值。h.request包含請求,h.client_address包含客戶端地址,h.server包含調用處理程序的實例。對於TCP之類的數據流服務,h.request屬性是套接字對象。對於數據報服務,它是包含收到數據的字節字符串。全部的邏輯函數都寫在這個方法裏。
二、h.setup() 該方法在handle()以前調用。默認狀況下,它不執行任何操做。若是但願服務器實現更多鏈接設置(如創建SSL鏈接),能夠在這裏實現。
三、h.finish() 調用本方法能夠在執行完handle()以後執行清除操做。默認狀況下,它不執行任何操做。若是setup()和handle()方法都不生成異常,則無需調用該方法。
若是知道應用程序只能操縱面向數據流的鏈接(如TCP),那麼應從StreamRequestHandler繼承,而不是BaseRequestHandler。StreamRequestHandler類設置了兩個屬性,h.wfile是將數據寫入客戶端的類文件對象,h.rfile是從客戶端讀取數據的類文件對象。
若是要編寫針對數據包操做的處理程序並將響應持續返回發送方,那麼它應當從DatagramRequestHandler繼承。它提供的類接口與StramRequestHandler相同。
2、服務器
socketserver 類提供的server類之間的關係
+------------+
| BaseServer | -----原生類(基礎類)
+------------+
|
v
提供tcp鏈接的一些服務 與UDPserver只差一個參數(address_family = socket.AF_UNIX)
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
提供tcp鏈接的一些服務 與TCPserver只差一個參數(address_family = socket.AF_UNIX)
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
要使用處理程序,必須將其插入到服務器對象。定義了四個基本的服務器類。
(1)TCPServer(address,handler) 支持使用IPv4的TCP協議的服務器,address是一個(host,port)元組。Handler是BaseRequestHandler或StreamRequestHandler類的子類的實例。
(2)UDPServer(address,handler) 支持使用IPv4的UDP協議的服務器,address和handler與TCPServer中相似。
(3)UnixStreamServer(address,handler) 使用UNIX域套接字實現面向數據流協議的服務器,繼承自TCPServer。
(4)UnixDatagramServer(address,handler) 使用UNIX域套接字實現數據報協議的服務器,繼承自UDPServer。
全部四個服務器類的實例都有如下方法和變量:
一、s.socket 用於傳入請求的套接字對象。
二、s.sever_address 監聽服務器的地址。如元組("127.0.0.1",80)
三、s.RequestHandlerClass 傳遞給服務器構造函數並由用戶提供的請求處理程序類。
四、s.serve_forever() 處理無限的請求
五、s.shutdown() 中止serve_forever()循環
六、s.fileno() 返回服務器套接字的整數文件描述符。該方法能夠有效地經過輪詢操做(如select()函數)使用服務器實例。
3、定義自定義服務器
服務器每每須要特殊的配置來處理不一樣的網絡地址族、超時期、併發和其餘功能,能夠經過繼承上面四個基本服務器類來自行定義。
能夠經過混合類得到更多服務器功能,這也是經過進程或線程分支添加併發行的方法。爲了實現併發性,定義瞭如下類:
(1)ForkingMixIn 將UNIX進程分支添加到服務器的混合方法,使用該方法可讓服務器服務多個客戶。
(2)ThreadingMixIn 修改服務器的混合類,可使用線程服務多個客戶端。
要向服務器添加這些功能,可使用多重繼承,其中首先列出混了類。
因爲併發服務器很經常使用,爲了定義它,SocketServer預約義瞭如下服務器類:
(1)ForkingUDPServer(address,handler)
(2)ForkingTCPServer(address,handler)
(3)ThreadingUDPServer(address,handler)
(4)ThreadingTCPServer(address,handler)
建立socketserver服務端與客戶端
import socketserver print("......") class Myserver(socketserver.BaseRequestHandler): def handle(self): #源碼以定義好的方法,只需寫具體的邏輯方法 print("服務器啓動:") while True: conn = self.request print(self.client_address) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) inp = input(">>>>:") conn.send(bytes(inp,"utf8")) conn.close() if __name__ == "__main__": server = socketserver.ThreadingTCPServer(("127.0.0.1",8010),Myserver) #元組建立對象,綁定address(),實現監聽(源碼默認爲5) server.serve_forever() #程序啓動
import socket sk = socket.socket() address = ("127.0.0.1",8010) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) date = sk.recv(1024) print(str(date,"utf8")) sk.close()