python網(wǎng)絡編程基礎(連載)03 socket-udp

2 socket模塊-UDP

gitbook鏈接:用python帶你進入AI中的深度學習技術(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)來實現(xiàn)網(wǎng)絡功能。套接字是一種類文件對象,它使得程序能夠接受客戶端的連接或者建立對客戶端的連接,用以發(fā)送和接收數(shù)據(jù)。不論是客戶端程序還是服務器端程序,為了進行網(wǎng)絡通信,都要創(chuàng)建套接字對象。本章講解UDP協(xié)議用python套接字模塊的實現(xiàn)。

2.1 UDP

2.1.1 udp定義

UDP 是User Datagram Protocol的簡稱, 中文名是用戶數(shù)據(jù)報協(xié)議,是OSI(Open System Interconnection,開放式系統(tǒng)互聯(lián)) 參考模型中一種無連接的傳輸層協(xié)議,提供面向事務的簡單不可靠信息傳送服務,IETF RFC 768是UDP的正式規(guī)范。UDP在IP報文的協(xié)議號是17。UDP有不提供數(shù)據(jù)包分組、組裝和不能對數(shù)據(jù)包進行排序的缺點,也就是說,當報文發(fā)送之后,是無法得知其是否安全完整到達的。UDP用來支持那些需要在計算機之間傳輸數(shù)據(jù)的網(wǎng)絡應用。包括網(wǎng)絡視頻會議系統(tǒng)在內(nèi)的眾多的客戶/服務器模式的網(wǎng)絡應用都需要使用UDP協(xié)議。

2.1.2 udp特性

(1) UDP是一個無連接協(xié)議,傳輸數(shù)據(jù)之前源端和終端不建立連接,當它想傳送時就簡單地去抓取來自應用程序的數(shù)據(jù),并盡可能快地把它扔到網(wǎng)絡上。在發(fā)送端,UDP傳送數(shù)據(jù)的速度僅僅是受應用程序生成數(shù)據(jù)的速度、計算機的能力和傳輸帶寬的限制;在接收端,UDP把每個消息段放在隊列中,應用程序每次從隊列中讀一個消息段。 (2) 由于傳輸數(shù)據(jù)不建立連接,因此也就不需要維護連接狀態(tài),包括收發(fā)狀態(tài)等,因此一臺服務機可同時向多個客戶機傳輸相同的消息。 (3) UDP信息包的標題很短,只有8個字節(jié),相對于TCP的20個字節(jié)信息包的額外開銷很小。 (4) 吞吐量不受擁擠控制算法的調(diào)節(jié),只受應用軟件生成數(shù)據(jù)的速率、傳輸帶寬、源端和終端主機性能的限制。 (5)UDP使用盡最大努力交付,即不保證可靠交付,因此主機不需要維持復雜的鏈接狀態(tài)表(這里面有許多參數(shù))。 (6)UDP是面向報文的。發(fā)送方的UDP對應用程序交下來的報文,在添加首部后就向下交付給IP層。既不拆分,也不合并,而是保留這些報文的邊界,因此,應用程序需要選擇合適的報文大小。

雖然UDP是一個不可靠的協(xié)議,但它是分發(fā)信息的一個理想?yún)f(xié)議。例如,在屏幕上報告股票市場、在屏幕上顯示航空信息等等。UDP也用在路由信息協(xié)議RIP(Routing Information Protocol)中修改路由表。在這些應用場合下,如果有一個消息丟失,在幾秒之后另一個新的消息就會替換它。UDP廣泛用在多媒體應用中,例如,Progressive Networks公司開發(fā)的RealAudio軟件,它是在因特網(wǎng)上把預先錄制的或者現(xiàn)場音樂實時傳送給客戶機的一種軟件,該軟件使用的RealAudio audio-on-demand protocol協(xié)議就是運行在UDP之上的協(xié)議,大多數(shù)因特網(wǎng)電話軟件產(chǎn)品也都運行在UDP之上。

2.2 socket模塊函數(shù)

python中實現(xiàn)套接字的基本模塊為socket。一般公共socket()函數(shù)來創(chuàng)建套接字,并進行網(wǎng)絡通信。要使用socket需要導入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默認值代表ipv4。 type為套接字類型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;SOCK_STREAM為TCP協(xié)議使用的類型,SOCK_DGRAM為UDP使用的類型。 proto為協(xié)議類型,默認為0 。

常見的socket對象常用的方法有:

bind(address)

其參數(shù)address是由ip和端口組成的元組,如('127.0.0.1', 8888) 。如果ip地址為空,則表示本機,它的作用為綁定端口,使該程序在運行時使用操作系統(tǒng)的固定指定端口。

listen(backlog)

其參數(shù)backlog是指在拒絕連接之前,操作系統(tǒng)允許此程序的最大掛起連接數(shù)量。最小值為0.

accept()

等待進入連接,并返回一個由新建的與客戶端的socket連接和客戶端地址組成的元組,其中客戶的地址是由客戶端ip地址和端口組成的元組。

close()

關(guān)閉套接字,停止連接。

recv(buffersize[, flag])

TCP用于接收遠程連接發(fā)來的信息,并獲取該信息,python3中為bytes類型。buffersize為接收緩沖區(qū)的大小。阻塞函數(shù)

send(data[, flags])

TCP用于發(fā)送數(shù)據(jù),data為bytes類型,返回值為已經(jīng)發(fā)送的字節(jié)數(shù)。

recvfrom(buffersize[, flag])

UDP用于接收遠程連接發(fā)來的信息,并獲取該信息,python3中為bytes類型。buffersize為接收緩沖區(qū)的大小。阻塞函數(shù)

sendto(data[, flags])

UDP用于發(fā)送數(shù)據(jù),data為bytes類型,返回值為已經(jīng)發(fā)送的字節(jié)數(shù)。

2.3 UDP套接字流程

這幾章為了更好的無負擔學習套接字的客戶端和服務端程序?qū)W習,我們借助非常有名的網(wǎng)絡調(diào)試助手來充當服務器或者客戶端進行配合演示。網(wǎng)絡調(diào)試助手的具體使用可以查看UDP操作https://jingyan.baidu.com/article/20b68a88a9c056796dec625e.html和TCP操作:https://jingyan.baidu.com/article/148a1921dc93e74d71c3b1d7.html。 本書采用mac的網(wǎng)絡調(diào)試助手演示,其他系統(tǒng)的可以查看百度鏈接。

udp01.png

udp02.png

根據(jù)UDP協(xié)議的無連接特點,一般客戶端僅僅需要創(chuàng)建套接字、數(shù)據(jù)收發(fā)、關(guān)閉套接字三個部分就可以完成了。服務器由于需要給眾多客戶端一個明確的連接地址和端口,所以需要額外的綁定端口操作。下圖為UDP客戶端和UDP服務器之間的通信流程。

udp03.png

udp通信模型中,在通信開始之前,不需要建立相關(guān)的鏈接,只需要發(fā)送數(shù)據(jù)即可,類似于生活中,"寫信"/“發(fā)短信“。


socket_udp_xinxiang.png

2.4 UDP客戶端實現(xiàn)

需求實現(xiàn):

向服務器發(fā)送一條數(shù)據(jù),并接收來自服務器的數(shù)據(jù),并打印

根據(jù)流程圖書寫模塊代碼

導入套接字模塊

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  # 導入模塊

?

address = ('192.168.234.129', 8080)  # 服務器地址為192.168.234.129,端口號為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()

實現(xiàn)結(jié)果


socket_udp_client.png

本節(jié)通過mac上運行我們實例中的udp客戶端程序,在虛擬機上用linux上打開網(wǎng)絡助手作為UDP服務器。其中,本地mac的ip為192.168.234.1,在運行時系統(tǒng)自動分配的端口號為58505;虛擬機上的linux系統(tǒng)的ip為192.168.234.129,設定服務器端口號為8080。 先讓服務端運行(點擊連接網(wǎng)絡),然后運行程序,可以看到linux上的網(wǎng)絡調(diào)試助手接收到的數(shù)據(jù)為net01_udp_client.py;然后通過設置網(wǎng)絡調(diào)試助手上的目標ip和端口為我們的udp客戶端程序運行的ip和端口(192.168.234.1, 58505),寫入數(shù)據(jù)(我已經(jīng)接收到數(shù)據(jù)了),點擊發(fā)送;在udp客戶端可以看到成功接收到了來自網(wǎng)絡調(diào)試助手發(fā)送的數(shù)據(jù)和相關(guān)地址信息。這樣我們就簡單的實現(xiàn)了udp客戶端的收發(fā)功能和語法學習。

2.5 UDP服務器端實現(xiàn)

需求實現(xiàn):

從客戶端收到一條數(shù)據(jù)后,在數(shù)據(jù)頭增加’來自服務器‘字符串,然后一起轉(zhuǎn)發(fā)回客戶端,然后關(guān)閉服務器套接字。

根據(jù)流程圖書寫模塊代碼

導入套接字模塊

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)  # 地址:設定服務器要使用端口8888

sock.bind(address)  # 綁定端口

?

recv_data = sock.recvfrom(1024)  # 接收數(shù)據(jù)

send_data = '來自服務器' + recv_data[0].decode()  # 數(shù)據(jù)處理,增加'來自服務器'

sock.sendto(send_data.encode('utf-8'), recv_data[1])  # 發(fā)送數(shù)據(jù)

?

sock.close()  # 關(guān)閉套接字

實現(xiàn)結(jié)果

本節(jié)通過mac上運行我們實例中的udp服務端程序,在虛擬機上用linux上打開網(wǎng)絡助手作為UDP客戶端。其中,本地mac的ip為192.168.234.1,設定綁定的端口號為8888;虛擬機上的linux系統(tǒng)的ip為192.168.234.129。 先運行程序,然后通過網(wǎng)絡調(diào)試助手指定發(fā)送ip和端口,發(fā)送數(shù)據(jù)’服務器你好‘,可以看到linux上的網(wǎng)絡調(diào)試助手接收到的數(shù)據(jù)為’來自服務器服務器你好‘。這樣我們就簡單的實現(xiàn)了udp服務端的收發(fā)功能和語法學習。


socket_udp_server.png

如果把上一節(jié)的客戶端所要發(fā)送的目標地址修改為本節(jié)所創(chuàng)建的服務器地址('192.168.234.1', 8888),我們會發(fā)現(xiàn)它們實現(xiàn)了通信:同一操作系統(tǒng)不同進程間的通信。

# address = ('192.168.234.129', 8080)  # 服務器地址為192.168.234.129,端口號為8080

address = ('192.168.234.1', 8888) # 和net02_udp_server服務器進行通信
socket_udp_for_self.png

2.6 實例:UDP簡易版聊天工具實現(xiàn)

需求實現(xiàn):

編寫1個程序,有2個功能:1.獲取鍵盤數(shù)據(jù),并將其發(fā)送給指定方,2.接收數(shù)據(jù)并顯示。進行簡單選擇以上的2個功能調(diào)用實現(xiàn)相應的功能。

根據(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ù)并打印


socket_udp_chat_1.png

完整代碼

'''net03_udp_chat.py'''

import socket

?

?

def send_message():

   send_data = input('請輸入要發(fā)送的消息:\n')

   send_ip = input('請輸入要發(fā)送的ip:\n')

   send_port = input('請輸入要發(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)  # 地址:設定服務器要使用端口8888

   sock.bind(address)  # 綁定端口

?

   # 功能菜單顯示

   print('*' * 30)

   print('1、發(fā)送數(shù)據(jù)')

   print('2、接收數(shù)據(jù)')

   print('*' * 30)

   fun_num = input('請選擇并輸入指定數(shù)字:\n')  # 獲取鍵盤選項數(shù)據(jù)

?

   # 輸入判斷

   if fun_num == '1':

       send_message()

   elif fun_num == '2':

       recv_message()

   else:

       print('您輸入的數(shù)據(jù)有誤!程序結(jié)束')

實現(xiàn)結(jié)果


socket_udp_chat.png

本節(jié)運行我們實例中的udp聊天器程序,在虛擬機上用linux上打開網(wǎng)絡助手作為UDP客戶端。其中,我們的udp聊天器的ip為192.168.234.1,設定綁定的端口號為8888;虛擬機上的linux系統(tǒng)的ip為192.168.234.129,設定服務器端口號為8080。 從下圖的結(jié)果可以看到,選擇不同的選項會進入不同的模塊,來完成指定功能。此時我們便完成了udp的基本學習。

2.7 小結(jié)

socket_udp_summary.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容