一、udp簡介 udp 用戶數據報協議,是一個無連接的簡單的面向數據報的運輸層協議。 udp不提供可靠性,它只是把應用程式傳給IP層的數據報發送出去,但是並不能保證它們能到達目的地。 udp在傳輸數據報前不用在客戶和伺服器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。 udp是一種面向無 ...
一、udp簡介
- udp --- 用戶數據報協議,是一個無連接的簡單的面向數據報的運輸層協議。
- udp不提供可靠性,它只是把應用程式傳給IP層的數據報發送出去,但是並不能保證它們能到達目的地。
- udp在傳輸數據報前不用在客戶和伺服器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。
- udp是一種面向無連接的協議,每個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
二、udp特點:
udp是面向無連接的通訊協議,udp數據包括目的埠號和源埠號信息,由於通訊不需要連接,所以可以實現廣播發送。 udp傳輸數據時有大小限制,每個被傳輸的數據報必須限定在64KB之內。 udp是一個不可靠的協議,發送方所發送的數據報並不一定以相同的次序到達接收方。udp是面向消息的協議,通信時不需要建立連接,數據的傳輸自然是不可靠的,udp一般用於多點通信和實時的數據業務,比如:
- 語音廣播
- TFTP(簡單文件傳送)
- SNMP(簡單網路管理協議)
- RIP(路由信息協議,如報告股票市場,航空信息)
- DNS(功能變數名稱解釋)
三、udp網路程式-發送數據
創建一個udp客戶端程式的流程是簡單,具體步驟如下:
- 創建客戶端套接字
- 發送/接收數據
- 關閉套接字
代碼如下:
#coding=utf-8 from socket import * #1、創建socket套接字 #socket(參數1,參數2) #參數1 = AF_INET固定的 #參數2 = SOCK_DGRAM表示udp,上篇文章中說過SOCK_STREM表示tcp udpSocket = socket(AF_INET,SOCK_DGRAM) #2、準備接收方的地址 sendAddress = ("192.168.100.101",8080) #3、從鍵盤輸入需要發送的數據 sendData = input("請輸入要發送的數據:") #4、發送數據到指定電腦 udpSocket.sendto(sendData.encode(),sendAddress) #5、關閉socket套接字 udpSocket.close()
運行程式:
這個時候我就向我的另外一臺IP地址為:192.168.100.101 埠號為8080的程式發送了“我是侯哥”這一條消息。我們藉助於網路調試助手軟體用於測試,網路調試助手各個平臺的系統都有,大家可以自己下載使用。
說明:我的代碼是在windows電腦上運行的,我的網路調試助手是在Mac電腦上運行的,如果沒有兩臺電腦的,也可以使用虛擬機測試。
四、udp網路程式-接收數據
#coding=utf-8 from socket import * #1、創建socket套接字 udpSocket = socket(AF_INET,SOCK_DGRAM) #2、準備接收方的地址 sendAddress = ("192.168.100.101",8080) #3、從鍵盤輸入需要發送的數據 sendData = input("請輸入要發送的數據:") #4、發送數據到指定電腦 udpSocket.sendto(sendData.encode(),sendAddress) #5、等待接收對方發送的數據 receiveData = udpSocket.recvfrom(1024) #6、顯示對方發送的數據 print(receiveData) #7、關閉socket套接字 udpSocket.close()
運行程式:
五、udp網路程式-埠問題
會變的埠號:重新運行多次腳本,然後在“網路調試助手”中,看到的現象如下:
說明:
- 每重新運行一次網路程式,上圖中紅圈中的數字,不一樣的原因在於,這個數字標識這個網路程式,當重新運行時,如果沒有確定到底用哪個,系統預設會隨機分配
- 記住一點:這個網路程式在運行的過程中,這個就唯一標識這個程式,所以如果其他電腦上的網路程式如果想要向此程式發送數據,那麼就需要向這個數字(即埠)標識的程式發送即可
六、udp綁定信息
一般情況下,在一天電腦上運行的網路程式有很多,而各自用的埠號很多情況下不知道,為了不與其他的網路程式占用同一個埠號,往往在編程中,udp的埠號一般不綁定,但是如果需要做成一個伺服器端的程式的話,是需要綁定的。就像報警電話每天都在變,想必世界就會亂了,所以一般服務性的程式,往往需要一個固定的埠號,這就是所謂的埠綁定
綁定示例
#coding=utf-8 from socket import * #1、創建socket套接字 udpSocket = socket(AF_INET,SOCK_DGRAM) #2、綁定相關信息,如果一個網路程式不綁定,則系統會隨機分配 bindAddress = ("",7781)#ip地址和埠號,ip一般不用寫,表示本機的任何一個ip udpSocket.bind(bindAddress) #3、等待接收方發送消息 receiveData = udpSocket.recvfrom(1024) #4、顯示對方發送的數據 print(receiveData) #5、關閉socket套接字 udpSocket.close()
windows電腦發送信息
mac電腦接收信息如下:
說明:
- 一個udp網路程式,可以不綁定,此時操作系統會隨機進行分配一個埠,如果重新運行次程式埠可能會發生變化
- 一個udp網路程式,也可以綁定信息(ip地址,埠號),如果綁定成功,那麼操作系統用這個埠號來進行區別收到的網路數據是否是此進程的
七、udp網路通信過程
八、udp應用:多線程對話聊天實現
#coding=utf-8 from threading import Thread from socket import * #接收數據 def receiveInfo(): while True: receiveData = udpSocket.recvfrom(1024) print("<<%s:%s"%(str(receiveData[1]),str(receiveData[0]))) #發送數據 def sendInfo(): while True: sendData = input("") udpSocket.sendto(sendData.encode("gb2312"),(destIp,destPort)) udpSocket = None destIp = "" destPort = 0 def main(): global udpSocket global destIp global destPort destIp = input("對方的IP:") destPort = int(input("對方的Port:")) udpSocket = socket(AF_INET,SOCK_DGRAM) udpSocket.bind(("",4567))#這裡寫兩個()的原因是將("",4567)當做一個整體元組使用 tr = Thread(target = receiveInfo) ts = Thread(target = sendInfo) tr.start() ts.start() tr.join() ts.join() if __name__ == '__main__': main()
Mac電腦上執行程式如下:
windows電腦上運行網路調試助手如下:
從而就是實現了基於socket的udp的聊天功能。
九、udp應用:多線程聊天室的實現
程式基本流程:創建接收端socket ---> 創建發送到socket ---> 啟動接收端socket ---> 啟動發送端socket ---> 等待用戶輸入 ---> 接收用戶輸入併發送到廣播 ---> 接收信息並顯示。
# -*- coding:utf-8 -*- from socket import * from time import ctime, sleep import threading class ChatRoomPlus: def __init__(self): # 全局參數配置 self.encoding = "utf-8" # 使用的編碼方式 self.broadcastPort = 7788 # 廣播埠 # 創建廣播接收器 self.recvSocket = socket(AF_INET, SOCK_DGRAM) self.recvSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) self.recvSocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) self.recvSocket.bind(('', self.broadcastPort)) # 創建廣播發送器 self.sendSocket = socket(AF_INET, SOCK_DGRAM) self.sendSocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) # 其他 self.threads = [] def send(self): """發送廣播""" print("UDP發送器啟動成功...") self.sendSocket.sendto("***加入了聊天室".encode(self.encoding), ('255.255.255.255', self.broadcastPort)) while True: sendData = input("請輸入需要發送的消息:") self.sendSocket.sendto(sendData.encode(self.encoding), ('255.255.255.255', self.broadcastPort)) # print("【%s】%s:%s" % (ctime(), "我", sendData)) sleep(1) def recv(self): """接收廣播""" print("UDP接收器啟動成功...") while True: # 接收數據格式:(data, (ip, port)) recvData = self.recvSocket.recvfrom(1024) print("【%s】[%s : %s] : %s" % (ctime(), recvData[1][0], recvData[1][1], recvData[0].decode(self.encoding))) sleep(1) def start(self): """啟動線程""" t1 = threading.Thread(target=self.recv) t2 = threading.Thread(target=self.send) self.threads.append(t1) self.threads.append(t2) for t in self.threads: t.setDaemon(True) t.start() while True: pass if __name__ == "__main__": demo = ChatRoomPlus() demo.start()
運行效果:
Mac電腦上運行
linux電腦上運行程式
windows上運行程式