2 socket模塊-UDP
gitbook鏈接:用python帶你進(jìn)入AI中的深度學(xué)習(xí)技術(shù)領(lǐng)域https://www.gitbook.com/book/scrappyzhang/python_to_deeplearn/details
github鏈接:https://github.com/ScrappyZhang/python_web_Crawler_DA_ML_DL
TCP/IP協(xié)議中的TCP和UDP協(xié)議都是通過一種名為套接字(socket)來實(shí)現(xiàn)網(wǎng)絡(luò)功能。套接字是一種類文件對(duì)象痢畜,它使得程序能夠接受客戶端的連接或者建立對(duì)客戶端的連接励稳,用以發(fā)送和接收數(shù)據(jù)。不論是客戶端程序還是服務(wù)器端程序散休,為了進(jìn)行網(wǎng)絡(luò)通信单鹿,都要?jiǎng)?chuàng)建套接字對(duì)象馏予。本章講解UDP協(xié)議用python套接字模塊的實(shí)現(xiàn)天梧。
2.1 UDP
2.1.1 udp定義
UDP 是User Datagram Protocol的簡(jiǎn)稱, 中文名是用戶數(shù)據(jù)報(bào)協(xié)議霞丧,是OSI(Open System Interconnection呢岗,開放式系統(tǒng)互聯(lián)) 參考模型中一種無連接的傳輸層協(xié)議,提供面向事務(wù)的簡(jiǎn)單不可靠信息傳送服務(wù)蛹尝,IETF RFC 768是UDP的正式規(guī)范后豫。UDP在IP報(bào)文的協(xié)議號(hào)是17。UDP有不提供數(shù)據(jù)包分組突那、組裝和不能對(duì)數(shù)據(jù)包進(jìn)行排序的缺點(diǎn)挫酿,也就是說,當(dāng)報(bào)文發(fā)送之后愕难,是無法得知其是否安全完整到達(dá)的早龟。UDP用來支持那些需要在計(jì)算機(jī)之間傳輸數(shù)據(jù)的網(wǎng)絡(luò)應(yīng)用。包括網(wǎng)絡(luò)視頻會(huì)議系統(tǒng)在內(nèi)的眾多的客戶/服務(wù)器模式的網(wǎng)絡(luò)應(yīng)用都需要使用UDP協(xié)議猫缭。
2.1.2 udp特性
(1) UDP是一個(gè)無連接協(xié)議葱弟,傳輸數(shù)據(jù)之前源端和終端不建立連接,當(dāng)它想傳送時(shí)就簡(jiǎn)單地去抓取來自應(yīng)用程序的數(shù)據(jù)饵骨,并盡可能快地把它扔到網(wǎng)絡(luò)上翘悉。在發(fā)送端,UDP傳送數(shù)據(jù)的速度僅僅是受應(yīng)用程序生成數(shù)據(jù)的速度居触、計(jì)算機(jī)的能力和傳輸帶寬的限制妖混;在接收端,UDP把每個(gè)消息段放在隊(duì)列中轮洋,應(yīng)用程序每次從隊(duì)列中讀一個(gè)消息段制市。 (2) 由于傳輸數(shù)據(jù)不建立連接,因此也就不需要維護(hù)連接狀態(tài)弊予,包括收發(fā)狀態(tài)等祥楣,因此一臺(tái)服務(wù)機(jī)可同時(shí)向多個(gè)客戶機(jī)傳輸相同的消息。 (3) UDP信息包的標(biāo)題很短汉柒,只有8個(gè)字節(jié)误褪,相對(duì)于TCP的20個(gè)字節(jié)信息包的額外開銷很小。 (4) 吞吐量不受擁擠控制算法的調(diào)節(jié)碾褂,只受應(yīng)用軟件生成數(shù)據(jù)的速率兽间、傳輸帶寬、源端和終端主機(jī)性能的限制正塌。 (5)UDP使用盡最大努力交付嘀略,即不保證可靠交付,因此主機(jī)不需要維持復(fù)雜的鏈接狀態(tài)表(這里面有許多參數(shù))乓诽。 (6)UDP是面向報(bào)文的帜羊。發(fā)送方的UDP對(duì)應(yīng)用程序交下來的報(bào)文,在添加首部后就向下交付給IP層鸠天。既不拆分讼育,也不合并,而是保留這些報(bào)文的邊界稠集,因此奶段,應(yīng)用程序需要選擇合適的報(bào)文大小。
雖然UDP是一個(gè)不可靠的協(xié)議巍杈,但它是分發(fā)信息的一個(gè)理想?yún)f(xié)議忧饭。例如,在屏幕上報(bào)告股票市場(chǎng)筷畦、在屏幕上顯示航空信息等等词裤。UDP也用在路由信息協(xié)議RIP(Routing Information Protocol)中修改路由表。在這些應(yīng)用場(chǎng)合下鳖宾,如果有一個(gè)消息丟失吼砂,在幾秒之后另一個(gè)新的消息就會(huì)替換它。UDP廣泛用在多媒體應(yīng)用中鼎文,例如渔肩,Progressive Networks公司開發(fā)的RealAudio軟件,它是在因特網(wǎng)上把預(yù)先錄制的或者現(xiàn)場(chǎng)音樂實(shí)時(shí)傳送給客戶機(jī)的一種軟件拇惋,該軟件使用的RealAudio audio-on-demand protocol協(xié)議就是運(yùn)行在UDP之上的協(xié)議周偎,大多數(shù)因特網(wǎng)電話軟件產(chǎn)品也都運(yùn)行在UDP之上抹剩。
2.2 socket模塊函數(shù)
python中實(shí)現(xiàn)套接字的基本模塊為socket。一般公共socket()函數(shù)來創(chuàng)建套接字蓉坎,并進(jìn)行網(wǎng)絡(luò)通信澳眷。要使用socket需要導(dǎo)入socket模塊:import socket。一般使用socket.socket()函數(shù)來創(chuàng)建套接字蛉艾。其語法如下:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto)
其中: family為套接字家族名:AN_INET钳踊、AF_INET6、AF_UNIX勿侯、AF_CAN拓瞪、AF_RDS;AN_INET默認(rèn)值代表ipv4。 type為套接字類型:SOCK_STREAM助琐、SOCK_DGRAM祭埂、SOCK_RAW;SOCK_STREAM為TCP協(xié)議使用的類型,SOCK_DGRAM為UDP使用的類型弓柱。 proto為協(xié)議類型沟堡,默認(rèn)為0 。
常見的socket對(duì)象常用的方法有:
bind(address)
其參數(shù)address是由ip和端口組成的元組矢空,如('127.0.0.1', 8888) 航罗。如果ip地址為空,則表示本機(jī)屁药,它的作用為綁定端口粥血,使該程序在運(yùn)行時(shí)使用操作系統(tǒng)的固定指定端口。
listen(backlog)
其參數(shù)backlog是指在拒絕連接之前酿箭,操作系統(tǒng)允許此程序的最大掛起連接數(shù)量复亏。最小值為0.
accept()
等待進(jìn)入連接,并返回一個(gè)由新建的與客戶端的socket連接和客戶端地址組成的元組缭嫡,其中客戶的地址是由客戶端ip地址和端口組成的元組缔御。
close()
關(guān)閉套接字,停止連接妇蛀。
recv(buffersize[, flag])
TCP用于接收遠(yuǎn)程連接發(fā)來的信息耕突,并獲取該信息,python3中為bytes類型评架。buffersize為接收緩沖區(qū)的大小眷茁。阻塞函數(shù)
send(data[, flags])
TCP用于發(fā)送數(shù)據(jù),data為bytes類型纵诞,返回值為已經(jīng)發(fā)送的字節(jié)數(shù)上祈。
recvfrom(buffersize[, flag])
UDP用于接收遠(yuǎn)程連接發(fā)來的信息,并獲取該信息,python3中為bytes類型登刺。buffersize為接收緩沖區(qū)的大小籽腕。阻塞函數(shù)
sendto(data[, flags])
UDP用于發(fā)送數(shù)據(jù),data為bytes類型塘砸,返回值為已經(jīng)發(fā)送的字節(jié)數(shù)节仿。
2.3 UDP套接字流程
這幾章為了更好的無負(fù)擔(dān)學(xué)習(xí)套接字的客戶端和服務(wù)端程序?qū)W習(xí)晤锥,我們借助非常有名的網(wǎng)絡(luò)調(diào)試助手來充當(dāng)服務(wù)器或者客戶端進(jìn)行配合演示掉蔬。網(wǎng)絡(luò)調(diào)試助手的具體使用可以查看UDP操作https://jingyan.baidu.com/article/20b68a88a9c056796dec625e.html和TCP操作:https://jingyan.baidu.com/article/148a1921dc93e74d71c3b1d7.html。 本書采用mac的網(wǎng)絡(luò)調(diào)試助手演示矾瘾,其他系統(tǒng)的可以查看百度鏈接女轿。
根據(jù)UDP協(xié)議的無連接特點(diǎn),一般客戶端僅僅需要?jiǎng)?chuàng)建套接字壕翩、數(shù)據(jù)收發(fā)蛉迹、關(guān)閉套接字三個(gè)部分就可以完成了。服務(wù)器由于需要給眾多客戶端一個(gè)明確的連接地址和端口放妈,所以需要額外的綁定端口操作北救。下圖為UDP客戶端和UDP服務(wù)器之間的通信流程。
udp通信模型中芜抒,在通信開始之前珍策,不需要建立相關(guān)的鏈接,只需要發(fā)送數(shù)據(jù)即可宅倒,類似于生活中攘宙,"寫信"/“發(fā)短信“。
2.4 UDP客戶端實(shí)現(xiàn)
需求實(shí)現(xiàn):
向服務(wù)器發(fā)送一條數(shù)據(jù)拐迁,并接收來自服務(wù)器的數(shù)據(jù)蹭劈,并打印
根據(jù)流程圖書寫模塊代碼
導(dǎo)入套接字模塊
import socket
創(chuàng)建UDP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
發(fā)數(shù)據(jù)
sock.sendto(data, address)
收數(shù)據(jù)
sock.recvfrom(buffersize)
關(guān)閉套接字
sock.close()
完整代碼
'''net01_udp_client.py'''
import socket # 導(dǎo)入模塊
?
address = ('192.168.234.129', 8080) # 服務(wù)器地址為192.168.234.129,端口號(hào)為8080
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 創(chuàng)建套接字
?
send_data = 'net01_udp_client.py'
print('要發(fā)送的數(shù)據(jù)為', send_data)
?
sock.sendto(send_data.encode('utf-8'), address) # 發(fā)送數(shù)據(jù)為bytes類型
?
recv_data = sock.recvfrom(1024) # 接收到的數(shù)據(jù)為兩部分线召,recv_data[1]為數(shù)據(jù)發(fā)送端的地址铺韧,recv_data[2]為接收到的數(shù)據(jù)
print(recv_data[1], '傳送回來的數(shù)據(jù)為:', recv_data[0].decode('utf-8'))
?
sock.close()
實(shí)現(xiàn)結(jié)果
本節(jié)通過mac上運(yùn)行我們實(shí)例中的udp客戶端程序,在虛擬機(jī)上用linux上打開網(wǎng)絡(luò)助手作為UDP服務(wù)器缓淹。其中哈打,本地mac的ip為192.168.234.1,在運(yùn)行時(shí)系統(tǒng)自動(dòng)分配的端口號(hào)為58505;虛擬機(jī)上的linux系統(tǒng)的ip為192.168.234.129割卖,設(shè)定服務(wù)器端口號(hào)為8080前酿。 先讓服務(wù)端運(yùn)行(點(diǎn)擊連接網(wǎng)絡(luò)),然后運(yùn)行程序鹏溯,可以看到linux上的網(wǎng)絡(luò)調(diào)試助手接收到的數(shù)據(jù)為net01_udp_client.py罢维;然后通過設(shè)置網(wǎng)絡(luò)調(diào)試助手上的目標(biāo)ip和端口為我們的udp客戶端程序運(yùn)行的ip和端口(192.168.234.1, 58505),寫入數(shù)據(jù)(我已經(jīng)接收到數(shù)據(jù)了),點(diǎn)擊發(fā)送;在udp客戶端可以看到成功接收到了來自網(wǎng)絡(luò)調(diào)試助手發(fā)送的數(shù)據(jù)和相關(guān)地址信息肺孵。這樣我們就簡(jiǎn)單的實(shí)現(xiàn)了udp客戶端的收發(fā)功能和語法學(xué)習(xí)匀借。
2.5 UDP服務(wù)器端實(shí)現(xiàn)
需求實(shí)現(xiàn):
從客戶端收到一條數(shù)據(jù)后,在數(shù)據(jù)頭增加’來自服務(wù)器‘字符串平窘,然后一起轉(zhuǎn)發(fā)回客戶端吓肋,然后關(guān)閉服務(wù)器套接字。
根據(jù)流程圖書寫模塊代碼
導(dǎo)入套接字模塊
import socket
創(chuàng)建UDP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
綁定端口
sock.bind(address)
收數(shù)據(jù)
sock.recvfrom(buffersize)
發(fā)數(shù)據(jù)
sock.sendto(data, address)
關(guān)閉套接字
sock.close()
完整代碼
'''net02_udp_server.py'''
import socket
?
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
?
address = ('192.168.234.1', 8888) # 地址:設(shè)定服務(wù)器要使用端口8888
sock.bind(address) # 綁定端口
?
recv_data = sock.recvfrom(1024) # 接收數(shù)據(jù)
send_data = '來自服務(wù)器' + recv_data[0].decode() # 數(shù)據(jù)處理瑰艘,增加'來自服務(wù)器'
sock.sendto(send_data.encode('utf-8'), recv_data[1]) # 發(fā)送數(shù)據(jù)
?
sock.close() # 關(guān)閉套接字
實(shí)現(xiàn)結(jié)果
本節(jié)通過mac上運(yùn)行我們實(shí)例中的udp服務(wù)端程序是鬼,在虛擬機(jī)上用linux上打開網(wǎng)絡(luò)助手作為UDP客戶端。其中紫新,本地mac的ip為192.168.234.1,設(shè)定綁定的端口號(hào)為8888均蜜;虛擬機(jī)上的linux系統(tǒng)的ip為192.168.234.129。 先運(yùn)行程序芒率,然后通過網(wǎng)絡(luò)調(diào)試助手指定發(fā)送ip和端口囤耳,發(fā)送數(shù)據(jù)’服務(wù)器你好‘,可以看到linux上的網(wǎng)絡(luò)調(diào)試助手接收到的數(shù)據(jù)為’來自服務(wù)器服務(wù)器你好‘偶芍。這樣我們就簡(jiǎn)單的實(shí)現(xiàn)了udp服務(wù)端的收發(fā)功能和語法學(xué)習(xí)充择。
如果把上一節(jié)的客戶端所要發(fā)送的目標(biāo)地址修改為本節(jié)所創(chuàng)建的服務(wù)器地址('192.168.234.1', 8888),我們會(huì)發(fā)現(xiàn)它們實(shí)現(xiàn)了通信:同一操作系統(tǒng)不同進(jìn)程間的通信匪蟀。
# address = ('192.168.234.129', 8080) # 服務(wù)器地址為192.168.234.129椎麦,端口號(hào)為8080
address = ('192.168.234.1', 8888) # 和net02_udp_server服務(wù)器進(jìn)行通信
2.6 實(shí)例:UDP簡(jiǎn)易版聊天工具實(shí)現(xiàn)
需求實(shí)現(xiàn):
編寫1個(gè)程序,有2個(gè)功能:1.獲取鍵盤數(shù)據(jù)萄窜,并將其發(fā)送給指定方铃剔,2.接收數(shù)據(jù)并顯示。進(jìn)行簡(jiǎn)單選擇以上的2個(gè)功能調(diào)用實(shí)現(xiàn)相應(yīng)的功能查刻。
根據(jù)流程圖書寫模塊代碼
主程序 套接字創(chuàng)建與端口綁定
功能菜單:1键兜、發(fā)送數(shù)據(jù) 2、接收數(shù)據(jù)
功能調(diào)用:如果1穗泵,調(diào)用發(fā)送數(shù)據(jù)函數(shù)普气;如果2 調(diào)用接收數(shù)據(jù)函數(shù)
發(fā)送數(shù)據(jù)函數(shù) 輸入數(shù)據(jù)、指定方的ip和端口 發(fā)送數(shù)據(jù)
接收數(shù)據(jù)函數(shù) 接收數(shù)據(jù)并打印
完整代碼
'''net03_udp_chat.py'''
import socket
?
?
def send_message():
send_data = input('請(qǐng)輸入要發(fā)送的消息:\n')
send_ip = input('請(qǐng)輸入要發(fā)送的ip:\n')
send_port = input('請(qǐng)輸入要發(fā)送的端口:\n')
send_address = (send_ip, int(send_port))
sock.sendto(send_data.encode('utf-8'), send_address)
?
?
def recv_message():
recv_data = sock.recvfrom(1024) # 接收數(shù)據(jù)
print('從', recv_data[1], '接收的數(shù)據(jù)為:', recv_data[0].decode('utf-8'))
?
?
if __name__ == '__main__':
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
?
address = ('192.168.234.1', 8888) # 地址:設(shè)定服務(wù)器要使用端口8888
sock.bind(address) # 綁定端口
?
# 功能菜單顯示
print('*' * 30)
print('1佃延、發(fā)送數(shù)據(jù)')
print('2现诀、接收數(shù)據(jù)')
print('*' * 30)
fun_num = input('請(qǐng)選擇并輸入指定數(shù)字:\n') # 獲取鍵盤選項(xiàng)數(shù)據(jù)
?
# 輸入判斷
if fun_num == '1':
send_message()
elif fun_num == '2':
recv_message()
else:
print('您輸入的數(shù)據(jù)有誤!程序結(jié)束')
實(shí)現(xiàn)結(jié)果
本節(jié)運(yùn)行我們實(shí)例中的udp聊天器程序履肃,在虛擬機(jī)上用linux上打開網(wǎng)絡(luò)助手作為UDP客戶端仔沿。其中,我們的udp聊天器的ip為192.168.234.1,設(shè)定綁定的端口號(hào)為8888尺棋;虛擬機(jī)上的linux系統(tǒng)的ip為192.168.234.129封锉,設(shè)定服務(wù)器端口號(hào)為8080。 從下圖的結(jié)果可以看到,選擇不同的選項(xiàng)會(huì)進(jìn)入不同的模塊成福,來完成指定功能碾局。此時(shí)我們便完成了udp的基本學(xué)習(xí)。