網絡編程,是指讓在不一樣的電腦上的軟件可以進行數據傳遞,即進程之間的通訊。編程
例若有:隊列、同步(互斥鎖、條件變量等)等,這些通訊方式都是一臺機器上不一樣進程之間的通訊方式。服務器
首要解決的問題是如何惟一標識一個進程,不然通訊無從談起!網絡
在本地能夠經過進程PID來惟一標識一個進程,可是在網絡中這是行不通的。socket
其實TCP/IP協議族已經幫咱們解決了這個問題,網絡層的「ip地址」能夠惟一標識網絡中的主機,而傳輸層的「協議+端口」能夠惟一標識主機中的應用程序(進程)。tcp
這樣利用ip地址、協議、端口
就能夠標識網絡的進程了,網絡中的進程通訊就能夠利用這個標誌與其它進程進行交互。函數
socket(簡稱套接字
)是進程間通訊的一種方式,它與其餘進程間通訊的一個主要不一樣是:它能實現不一樣主機間的進程間通訊,咱們網絡上各類各樣的服務大多都是基於 Socket 來完成通訊的。例如咱們天天瀏覽網頁、QQ 聊天、收發 email 等等。spa
在 Python 中使用 socket 模塊的函數 socket :調試
socket.socket(AddressFamily, Type)
函數 socket.socket 建立一個 socket,返回該 socket 的描述符,該函數帶有兩個參數:code
示例:視頻
1 >>> import socket 2 >>> # 建立了一個TCP Socket 3 >>> tcp_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 >>> tcp_s 5 <socket.socket fd=548, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> 6 >>> # 建立了一個UDP Socket 7 >>> udp_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 8 >>> udp_s 9 <socket.socket fd=620, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0>
UDP(User Data Protocol)——用戶數據報協議,是一個無鏈接的簡單的面向數據報的運輸層協議。
UDP是面向消息的協議,通訊時不須要創建鏈接,數據的傳輸天然是不可靠的,UDP通常用於多點通訊和實時的數據業務,好比
UDP操做簡單,並且僅須要較少的監護,所以一般用於局域網高可靠性的分散系統中client/server應用程序。例如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就能夠了,這種狀況下顯然使用UDP會更合理一些。
建立一個udp客戶端程序的流程較爲簡單,具體步驟以下:
示例:
1 import socket 2 3 # 1. 建立套接字 4 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 5 6 # 2. 準備接收方的地址 7 sendAddr = ("192.168.3.4", 8080) 8 9 # 3. 從鍵盤獲取要發送的數據 10 sendData = input("請輸入要發送的數據:") 11 12 # 4. 發送數據到指定的電腦上 13 udpSocket.send(sendData.encode(), sendAddr) 14 # encode():將 str 轉爲 bytes。 15 # decode():將 bytes 轉爲 str。如咱們從網絡或磁盤上讀取了字節流,那麼讀到的數據就是 bytes。 16 17 # 5. 等待接收方發送過來的數據 18 recvData = udpSocket.recv(1024) # 1024表示本次接收數據的最大字節數 19 20 # 6. 顯示對方發送的數據 21 print(recvData) 22 23 # 7. 關閉套接字 24 udpSocket.close()
執行效果:
會變的端口號:
從新運行屢次腳本,而後在「網絡調試助手」中,看到的現象以下:
通常狀況下,在一臺電腦上運行的網絡程序會有不少,而各自用的端口號不少狀況下也不知道。爲了避免與其餘的網絡程序佔用同一個端口號,每每在編程中,udp的端口號通常不綁定。
可是若是須要作成一個接收方的程序的話,是須要綁定的。正如若是報警電話天天都在變,想必世界就會亂了。因此通常服務性的程序,每每須要一個固定的端口號,這就是所謂的端口綁定。
示例:
1 import socket 2 3 # 建立套接字 4 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 5 6 # 綁定本地的相關信息,若是一個網絡程序不綁定,系統就會隨機分配 7 binAddr = ("", 7788) # IP地址和端口號,IP通常不用寫,表示本機的任何一個IP 8 udpSocket.bind(binAddr) 9 10 # 接收方的地址信息 11 sendAddr = ("192.168.234.1", 8080) 12 13 # 發送數據 14 sendData = udpSocket.sendto("haha".encode(), sendAddr) 15 16 # 等待接收方發送過來的數據 17 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收數據的最大字節數 18 19 # 顯示對方發送的數據 20 print(recvData) 21 22 # 關閉套接字 23 udpSocket.close()
執行效果:
1 import socket 2 import time 3 4 5 # 建立套接字 6 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 8 # 綁定本地的相關信息,若是一個網絡程序不綁定,系統就會隨機分配 9 binAddr = ("", 7788) # IP地址和端口號,IP通常不用寫,表示本機的任何一個IP 10 udpSocket.bind(binAddr) 11 12 # 接收方的地址信息 13 sendAddr = ("192.168.234.1", 8080) 14 15 num = 1 16 17 def repeat(): 18 # 等待對方發送來的數據 19 recvData = udpSocket.recvfrom(1024) # 1024即本次接收數據的最大字節數 20 # 將接收的數據再次發送回去 21 udpSocket.sendto(recvData[0], sendAddr) 22 # 統計信息 23 print("已經將接收到的第%d個信息返回給對方,數據內容爲:%s" % (num, recvData[0].decode())) 24 25 # 若不使用 try..except... 重試機制,而會出現「遠程主機強制關閉鏈接」的報錯 26 while True: 27 try: 28 repeat() 29 except: 30 repeat() 31 32 # 關閉套接字 33 udpSocket.close()
注意,若出現「ConnectionResetError: [WinError 10054] 遠程主機強迫關閉了一個現有的鏈接」:出現這種緣由表明遠程過於頻繁,因此遠程懷疑是惡意攻擊。
可使用 try...except...重試,在報錯時從新調用該方法使其從新抓取,直至抓取成功。
執行效果:
模擬一個聊天室,顯示全部接收到的數據。
1 import socket 2 import time 3 4 5 # 建立套接字 6 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 8 # 綁定本地的相關信息,若是一個網絡程序不綁定,系統就會隨機分配 9 binAddr = ("", 7788) # IP地址和端口號,IP通常不用寫,表示本機的任何一個IP 10 udpSocket.bind(binAddr) 11 12 # 接收方的地址信息 13 sendAddr = ("192.168.234.1", 8080) 14 15 16 def repeat(): 17 # 等待對方發送來的數據 18 recvData = udpSocket.recvfrom(1024) # 1024即本次接收數據的最大字節數 19 # 打印消息記錄 20 print("【%s】%s:%s"%(time.ctime(), recvData[1][0], recvData[0].decode())) 21 22 23 while True: 24 try: 25 repeat() 26 except: 27 repeat() 28 29 30 # 關閉套接字 31 udpSocket.close()
執行效果: