練習題:37

目錄

Python題目

題目

題目分析

套接字概念剖析

通信原理分析

服務器 - 客戶端連接建立過程:

基于套接字通信的底層機制:

代碼實現

基于 TCP 的簡單服務器 - 客戶端通信示例

服務器端代碼(tcp_server.py)

客戶端代碼(tcp_client.py)

基于 UDP 的簡單服務器 - 客戶端通信示例

服務器端代碼(udp_server.py)

客戶端代碼(udp_client.py)

代碼解釋

基于 TCP 的代碼解釋

服務器端

導入模塊與創建套接字對象:

綁定 IP 地址和端口號并監聽:

接受客戶端連接并處理通信:

客戶端

導入模塊與創建套接字對象:

連接服務器并發送接收消息:

基于 UDP 的代碼解釋

服務器端

導入模塊與創建套接字對象:

綁定 IP 地址和端口號并監聽:

接收并處理客戶端消息:

客戶端

導入模塊與創建套接字對象:

發送消息并接收回復:

運行思路

基于 TCP 的代碼分析

服務器端代碼分析

導入模塊與創建套接字對象:

綁定 IP 地址和端口號并監聽:

接受客戶端連接并處理通信:

客戶端代碼分析

導入模塊與創建套接字對象:

連接服務器并發送接收消息:

基于 UDP 的代碼分析

服務器端代碼分析

導入模塊與創建套接字對象:

綁定 IP 地址和端口號并監聽:

接收并處理客戶端消息:

客戶端代碼分析

導入模塊與創建套接字對象:

發送消息并接收回復:

結束語

Python題目

題目

套接字是什么?為什么在服務器端宇客戶端建立了套接字就可以通信了?

題目分析

  • 套接字概念剖析

    • 定義理解:套接字(Socket)是網絡通信的基石,它是一種軟件抽象層,用于在不同主機之間的進程進行通信。可以把套接字想象成是電話插孔,不同主機上的進程通過這個 “插孔”(套接字)來建立連接,就像兩部電話通過插孔和線路連接起來進行通話一樣。在網絡編程中,它屏蔽了底層網絡協議(如 TCP/IP 協議族)的復雜細節,為程序員提供了一個相對簡單的接口來進行網絡通信。
    • 類型區分:主要分為流套接字(Stream Socket,基于 TCP 協議)和數據報套接字(Datagram Socket,基于 UDP 協議)。流套接字提供面向連接、可靠的字節流服務,就像打電話一樣,通信雙方先建立連接,然后按順序傳輸數據,數據不會丟失或亂序。數據報套接字則是無連接的、不可靠的通信方式,類似于發送短信,消息被封裝成一個個獨立的數據報發送,不保證數據一定能到達,也不保證順序。
  • 通信原理分析

    • 服務器 - 客戶端連接建立過程
      • 服務器端套接字創建與監聽:服務器首先創建一個套接字,這個套接字綁定到一個特定的 IP 地址和端口號(IP 地址用于標識服務器在網絡中的位置,端口號用于區分不同的服務)。然后,服務器通過這個套接字開始監聽客戶端的連接請求。例如,一個 Web 服務器監聽在 80 端口(HTTP 服務默認端口)等待客戶端瀏覽器的連接請求。
      • 客戶端套接字創建與連接請求:客戶端同樣創建一個套接字,然后使用服務器的 IP 地址和端口號向服務器發送連接請求。這個請求通過網絡傳輸,當服務器監聽到這個請求后,就會接受這個請求,從而在服務器和客戶端之間建立起一個連接。
    • 基于套接字通信的底層機制
      • 協議支持:一旦建立連接(對于流套接字),TCP/IP 協議就會確保數據在兩個套接字之間可靠地傳輸。它通過一系列機制,如三次握手建立連接、數據確認和重傳、流量控制等來保證數據的完整性和順序性。例如,當客戶端發送一個數據包時,TCP 協議會在數據包中添加序列號等信息,服務器收到后會發送確認信息,這樣就保證了數據傳輸的可靠性。
      • 數據傳輸通道形成:套接字在連接建立后,就像在服務器和客戶端之間建立了一條虛擬的數據傳輸通道。雙方可以通過這個通道發送和接收數據,數據以字節流(對于流套接字)或數據報(對于數據報套接字)的形式在通道中傳輸。例如,客戶端可以將用戶輸入的信息通過套接字發送給服務器,服務器接收到數據后進行處理,并將結果通過套接字返回給客戶端。

代碼實現

基于 TCP 的簡單服務器 - 客戶端通信示例

服務器端代碼(tcp_server.py
import socket# 創建套接字對象,AF_INET表示使用IPv4地址族,SOCK_STREAM表示使用TCP協議(流套接字)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 綁定IP地址和端口號,這里使用本地回環地址127.0.0.1和端口8888
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)# 開始監聽,參數5表示允許的最大連接數
server_socket.listen(5)
print('服務器已啟動,正在監聽端口8888...')while True:# 接受客戶端連接,返回一個新的套接字對象(用于與該客戶端通信)和客戶端地址client_socket, client_address = server_socket.accept()print(f'接受來自 {client_address} 的連接')try:# 接收客戶端發送的數據,最多接收1024字節data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')# 處理客戶端消息,這里簡單將消息轉換為大寫后返回給客戶端response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已將處理后的消息發送回客戶端')except:print('接收或發送數據時出現錯誤')finally:# 關閉與該客戶端的套接字連接client_socket.close()
客戶端代碼(tcp_client.py
import socket# 創建套接字對象,同樣使用IPv4地址族和TCP協議
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 服務器的IP地址和端口號,要與服務器端綁定的一致
server_address = ('127.0.0.1', 8888)try:# 連接服務器client_socket.connect(server_address)message = "Hello, Server!"# 向服務器發送消息,需先將字符串編碼為字節流client_socket.send(message.encode('utf-8'))print(f'已向服務器發送消息: {message}')# 接收服務器返回的消息,最多接收1024字節data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')
except:print('連接服務器或通信過程中出現錯誤')
finally:# 關閉客戶端套接字client_socket.close()

基于 UDP 的簡單服務器 - 客戶端通信示例

服務器端代碼(udp_server.py
import socket# 創建套接字對象,AF_INET表示使用IPv4地址族,SOCK_DGRAM表示使用UDP協議(數據報套接字)
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 綁定IP地址和端口號,這里使用本地回環地址127.0.0.1和端口9999
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)print('UDP服務器已啟動,正在監聽端口9999...')while True:# 接收客戶端發送的數據和客戶端地址,最多接收1024字節data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')# 處理客戶端消息,這里簡單將消息轉換為大寫后返回給客戶端response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已將處理后的消息發送回客戶端')
客戶端代碼(udp_client.py
import socket# 創建套接字對象,使用IPv4地址族和UDP協議
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 服務器的IP地址和端口號,要與服務器端綁定的一致
server_address = ('127.0.0.1', 9999)message = "Hello, UDP Server!"
# 向服務器發送消息,需先將字符串編碼為字節流
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服務器發送消息: {message}')# 接收服務器返回的消息,最多接收1024字節
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')# 關閉客戶端套接字
client_socket.close()

代碼解釋

基于 TCP 的代碼解釋

服務器端
  • 導入模塊與創建套接字對象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 首先通過?import socket?導入 Python 的?socket?模塊,該模塊提供了進行網絡套接字編程所需的各種函數和類等資源。
  • 然后調用?socket.socket()?函數創建一個套接字對象?server_socket,參數?socket.AF_INET?表示使用 IPv4 地址族,socket.SOCK_STREAM?表示使用 TCP 協議(即創建的是流套接字),這個套接字將作為服務器與客戶端進行通信的基礎接口。
  • 綁定 IP 地址和端口號并監聽
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)
server_socket.listen(5)
print('服務器已啟動,正在監聽端口8888...')
  • 定義?server_address?元組,其中第一個元素?'127.0.0.1'?是 IP 地址,這里使用本地回環地址(常用于在本地機器上測試網絡程序,意味著只有本機上的程序可以訪問該服務器),第二個元素?8888?是端口號(可以根據實際需求選擇合適的未被占用的端口)。
  • 調用?server_socket.bind(server_address)?方法將創建好的套接字綁定到指定的 IP 地址和端口號上,這樣服務器就在這個網絡端點上等待客戶端的連接請求了。
  • 接著調用?server_socket.listen(5)?方法開始監聽客戶端的連接請求,參數?5?表示服務器允許的最大連接數,即同時可以有多少個客戶端連接到服務器等待處理。
  • 接受客戶端連接并處理通信
while True:client_socket, client_address = server_socket.accept()print(f'接受來自 {client_address} 的連接')try:data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已將處理后的消息發送回客戶端')except:print('接收或發送數據時出現錯誤')finally:client_socket.close()
  • 使用?while True?循環使得服務器可以持續處理多個客戶端的連接請求。在循環內部,調用?server_socket.accept()?方法,該方法會阻塞程序執行,直到有客戶端連接過來,當有客戶端連接時,它會返回一個新的套接字對象?client_socket(這個套接字專門用于與該客戶端進行后續的通信)以及客戶端的地址?client_address(包含客戶端的 IP 地址和端口號)。
  • 進入?try?塊嘗試接收客戶端發送的數據,調用?client_socket.recv(1024)?方法從與客戶端連接的套接字接收數據,參數?1024?表示最多接收 1024 字節的數據。如果接收到了數據,通過?data.decode('utf-8')?將接收到的字節數據解碼為字符串形式賦值給?message?變量,并打印出來。
  • 接著對收到的消息進行處理,這里簡單地將消息轉換為大寫形式,賦值給?response?變量,然后調用?client_socket.send(response.encode('utf-8'))?方法將處理后的消息編碼為字節流并發送回客戶端,同時打印發送提示信息。
  • 如果在接收或發送數據過程中出現錯誤,except?塊會捕獲異常并打印錯誤提示信息。
  • 無論是否出現錯誤,在?finally?塊中都會調用?client_socket.close()?方法關閉與當前客戶端的套接字連接,釋放相關資源,準備處理下一個客戶端的連接。
客戶端
  • 導入模塊與創建套接字對象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 同樣先通過?import socket?導入?socket?模塊,然后調用?socket.socket()?函數創建一個套接字對象?client_socket,使用的參數也是?socket.AF_INET(IPv4 地址族)和?socket.SOCK_STREAM(TCP 協議),用于與服務器進行通信。
  • 連接服務器并發送接收消息
server_address = ('127.0.0.1', 8888)
try:client_socket.connect(server_address)message = "Hello, Server!"client_socket.send(message.encode('utf-8'))print(f'已向服務器發送消息: {message}')data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')
except:print('連接服務器或通信過程中出現錯誤')
finally:client_socket.close()
  • 定義?server_address?元組,指定要連接的服務器的 IP 地址(這里同樣是本地回環地址?127.0.0.1)和端口號(8888),要與服務器端綁定的地址和端口一致。
  • 調用?client_socket.connect(server_address)?方法向服務器發起連接請求,如果連接成功,程序繼續往下執行。
  • 定義要發送給服務器的消息?message,然后調用?client_socket.send(message.encode('utf-8'))?方法將消息編碼為字節流并發送給服務器,同時打印發送提示信息。
  • 接著調用?client_socket.recv(1024)?方法從服務器接收回復消息,最多接收 1024 字節的數據,若接收到了數據,將其解碼為字符串形式賦值給?response?變量,并打印出來。
  • 如果在連接服務器或通信過程中出現錯誤,except?塊會捕獲異常并打印相應的錯誤提示信息。
  • 最后,在?finally?塊中調用?client_socket.close()?方法關閉客戶端套接字,釋放資源。

基于 UDP 的代碼解釋

服務器端
  • 導入模塊與創建套接字對象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 還是先通過?import socket?導入?socket?模塊,然后調用?socket.socket()?函數創建套接字對象?server_socket,這次參數使用?socket.AF_INET(IPv4 地址族)和?socket.SOCK_DGRAM(表示使用 UDP 協議,即創建的數據報套接字),用于基于 UDP 協議進行通信。
  • 綁定 IP 地址和端口號并監聽
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)
print('UDP服務器已啟動,正在監聽端口9999...')
  • 定義?server_address?元組,包含本地回環地址?'127.0.0.1'?和端口號?9999,用于指定服務器監聽的網絡端點。
  • 調用?server_socket.bind(server_address)?方法將套接字綁定到這個地址和端口上,使服務器可以在該端點接收客戶端發送的數據報。
  • 接收并處理客戶端消息
while True:data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已將處理后的消息發送回客戶端')
  • 通過?while True?循環持續等待接收客戶端發送的數據報。在循環內,調用?server_socket.recvfrom(1024)?方法接收客戶端發送的數據報,這個方法會阻塞程序執行,直到接收到數據報為止,它返回接收到的數據(字節形式)和客戶端的地址(包含客戶端的 IP 地址和端口號)。
  • 如果接收到了數據,將其解碼為字符串形式賦值給?message?變量并打印出來。
  • 對消息進行處理(這里同樣是轉換為大寫形式),得到?response?變量,然后調用?server_socket.sendto(response.encode('utf-8'), client_address)?方法將處理后的消息編碼為字節流,并根據客戶端的地址發送回客戶端,同時打印發送提示信息。
客戶端
  • 導入模塊與創建套接字對象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 導入?socket?模塊后創建一個套接字對象?client_socket,使用?socket.AF_INET(IPv4 地址族)和?socket.SOCK_DGRAM(UDP 協議)參數,用于向服務器發送和接收數據報。
  • 發送消息并接收回復
server_address = ('127.0.0.1', 9999)
message = "Hello, UDP Server!"
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服務器發送消息: {message}')
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')
client_socket.close()
  • 定義?server_address?元組,指定服務器的 IP 地址(127.0.0.1)和端口號(9999)。
  • 定義要發送給服務器的消息?message,然后調用?client_socket.sendto(message.encode('utf-8'), server_address)?方法將消息編碼為字節流,并按照指定的服務器地址發送出去,同時打印發送提示信息。
  • 調用?client_socket.recvfrom(1024)?方法等待接收服務器返回的數據報,接收到后將數據解碼為字符串賦值給?response?變量并打印出來。
  • 最后調用?client_socket.close()?方法關閉客戶端套接字,釋放資源。

運行思路

基于 TCP 的代碼分析

服務器端代碼分析
  • 導入模塊與創建套接字對象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 導入模塊import socket?語句將 Python 標準庫中的?socket?模塊引入程序,該模塊提供了操作網絡套接字的各種函數、類等資源,是實現網絡通信編程的基礎。若沒有正確導入此模塊,后續使用套接字相關功能時會引發?ModuleNotFoundError?異常,導致程序無法繼續運行。
  • 創建套接字對象socket.socket(socket.AF_INET, socket.SOCK_STREAM)?調用創建了一個套接字對象?server_socket。其中,socket.AF_INET?表示選用 IPv4 地址族,意味著這個套接字將基于 IPv4 網絡進行通信,適用于大多數常見的網絡環境;socket.SOCK_STREAM?表明創建的是流套接字,它基于 TCP 協議,提供面向連接、可靠的字節流通信服務,就像打電話一樣,通信雙方先建立穩定連接后再傳輸數據,且能保證數據的完整性和順序性。
  • 綁定 IP 地址和端口號并監聽
server_address = ('127.0.0.1', 8888)
server_socket.bind(server_address)
server_socket.listen(5)
print('服務器已啟動,正在監聽端口8888...')
  • 定義服務器地址server_address = ('127.0.0.1', 8888)?創建了一個包含 IP 地址和端口號的元組。127.0.0.1?是本地回環地址,代表本機,常用于在本地進行網絡程序的測試,只有本機上的其他程序可以通過這個地址訪問該服務器;8888?是端口號,它用于在網絡通信中區分不同的服務或應用程序,取值范圍是?0?到?65535,這里選擇?8888?作為自定義的服務端口(需確保該端口未被其他程序占用)。
  • 綁定套接字到地址server_socket.bind(server_address)?方法將之前創建的?server_socket?套接字綁定到指定的?server_address?上,使得服務器在網絡中通過這個特定的 IP 地址和端口號來接收客戶端的連接請求。若綁定的端口已被其他程序占用,會拋出?socket.error?異常,提示地址已在使用中。
  • 開始監聽客戶端連接server_socket.listen(5)?調用讓服務器套接字進入監聽狀態,參數?5?表示服務器允許的最大連接數,即同時可以有最多?5?個客戶端連接到服務器并處于等待處理的狀態。一旦進入監聽狀態,服務器就開始等待客戶端的連接請求,此時程序會阻塞在?accept?方法(后續調用處),直到有客戶端發起連接。
  • 接受客戶端連接并處理通信
while True:client_socket, client_address = server_socket.accept()print(f'接受來自 {client_address} 的連接')try:data = client_socket.recv(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')response = message.upper()client_socket.send(response.encode('utf-8'))print(f'已將處理后的消息發送回客戶端')except:print('接收或發送數據時出現錯誤')finally:client_socket.close()
  • 循環接受客戶端連接while True?構建了一個無限循環,使得服務器能夠持續不斷地處理多個客戶端的連接請求,只要服務器在運行,就會一直等待并處理新的連接。
  • 接受客戶端連接client_socket, client_address = server_socket.accept()?這行代碼是整個服務器通信流程的關鍵部分,accept?方法會阻塞程序執行,直到有客戶端連接過來。當有客戶端發起連接時,它會返回兩個值:一個新的套接字對象?client_socket(這個套接字專門用于與該客戶端進行后續的一對一通信,與之前用于監聽的?server_socket?不同)和客戶端的地址?client_address(包含客戶端的 IP 地址和端口號,以元組形式呈現,例如?('192.168.1.100', 56789)),通過打印?client_address?可以知曉客戶端的來源信息。
  • 接收客戶端數據:在?try?塊內,data = client_socket.recv(1024)?調用嘗試從與客戶端連接的?client_socket?套接字接收數據,參數?1024?表示最多接收?1024?字節的數據,這是一種常見的設置,可根據實際需求調整大小。如果成功接收到了數據(即?data?不為空),message = data.decode('utf-8')?會將接收到的字節數據按照 UTF-8 編碼格式解碼為字符串形式,賦值給?message?變量,然后通過?print(f'從客戶端收到消息: {message}')?將消息內容打印出來,方便查看客戶端發送的內容。
  • 處理并返回數據:接著,response = message.upper()?將接收到的消息轉換為大寫形式,作為對客戶端的響應內容。然后,client_socket.send(response.encode('utf-8'))?調用把處理后的響應消息?response?先編碼為字節流(使用 UTF-8 編碼,確保與接收時的解碼格式一致),再通過?client_socket?發送回客戶端,同時通過?print(f'已將處理后的消息發送回客戶端')?打印發送提示信息,告知服務器端已成功發送響應。
  • 異常處理與資源釋放:如果在接收或發送數據過程中出現任何錯誤(如網絡中斷、客戶端意外關閉連接等),except?塊會捕獲異常并通過?print('接收或發送數據時出現錯誤')?打印相應的錯誤提示信息,便于排查問題。無論是否出現錯誤,在?finally?塊中都會調用?client_socket.close()?方法關閉與當前客戶端的套接字連接,釋放相關資源,避免資源泄漏,并準備好處理下一個客戶端的連接請求。
客戶端代碼分析
  • 導入模塊與創建套接字對象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  • 導入模塊:同服務器端一樣,通過?import socket?導入?socket?模塊,為后續創建套接字和進行網絡通信操作提供必要的功能支持。
  • 創建套接字對象client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)?創建了一個客戶端使用的套接字對象?client_socket,同樣使用?socket.AF_INET(IPv4 地址族)和?socket.SOCK_STREAM(TCP 協議)參數,這使得客戶端創建的套接字與服務器端創建的用于監聽和接受連接的套接字類型匹配,能夠基于 TCP 協議建立可靠的連接進行通信。
  • 連接服務器并發送接收消息
server_address = ('127.0.0.1', 8888)
try:client_socket.connect(server_address)message = "Hello, Server!"client_socket.send(message.encode('utf-8'))print(f'已向服務器發送消息: {message}')data = client_socket.recv(1024)if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')
except:print('連接服務器或通信過程中出現錯誤')
finally:client_socket.close()
  • 定義服務器地址server_address = ('127.0.0.1', 8888)?定義了要連接的服務器的 IP 地址和端口號,這里的地址和端口號必須與服務器端綁定并監聽的地址端口完全一致,否則客戶端無法正確連接到服務器。
  • 連接服務器client_socket.connect(server_address)?調用嘗試向指定的?server_address?對應的服務器發起連接請求,若服務器正常監聽且網絡可達,連接會成功建立,程序繼續往下執行;若連接出現問題(如服務器未啟動、網絡故障、端口號錯誤等),會拋出異常,被后續的?except?塊捕獲處理。
  • 發送消息給服務器:成功連接服務器后,定義要發送的消息?message = "Hello, Server!",然后通過?client_socket.send(message.encode('utf-8'))?調用將消息先編碼為字節流(采用 UTF-8 編碼格式),再通過?client_socket?發送給服務器,同時通過?print(f'已向服務器發送消息: {message}')?打印發送提示信息,方便查看發送情況。
  • 接收服務器回復:接著調用?client_socket.recv(1024)?嘗試從服務器接收回復消息,最多接收?1024?字節的數據,若接收到了數據(即?data?不為空),則通過?response = data.decode('utf-8')?將接收到的字節數據按照 UTF-8 編碼格式解碼為字符串形式,賦值給?response?變量,并通過?print(f'從服務器收到回復: {response}')?打印出服務器回復的內容,實現客戶端與服務器之間的消息交互。
  • 異常處理與資源釋放:在整個連接服務器和通信過程中,如果出現任何錯誤(如連接超時、服務器意外關閉連接等),except?塊會捕獲異常并通過?print('連接服務器或通信過程中出現錯誤')?打印相應的錯誤提示信息,有助于定位問題所在。最后,無論通信是否成功,在?finally?塊中都會調用?client_socket.close()?方法關閉客戶端套接字,釋放相關資源,確保程序的資源管理規范和正常結束。

基于 UDP 的代碼分析

服務器端代碼分析
  • 導入模塊與創建套接字對象
import socketserver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 導入模塊:通過?import socket?導入?socket?模塊,為后續創建 UDP 套接字及相關網絡通信操作提供功能支持,若模塊導入失敗會導致程序無法使用套接字相關功能。
  • 創建套接字對象server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)?創建了一個基于 UDP 協議的數據報套接字對象?server_socket,這里使用?socket.AF_INET?表示 IPv4 地址族,說明基于 IPv4 網絡進行通信,而?socket.SOCK_DGRAM?明確了該套接字采用 UDP 協議,UDP 協議是一種無連接、不可靠的通信方式,類似于發送短信,數據被封裝成獨立的數據報進行發送,不保證數據一定能到達目的地,也不保證數據的順序性,但它具有開銷小、傳輸速度快的特點,適用于一些對實時性要求較高、對數據完整性要求相對不那么嚴格的場景。
  • 綁定 IP 地址和端口號并監聽
server_address = ('127.0.0.1', 9999)
server_socket.bind(server_address)
print('UDP服務器已啟動,正在監聽端口9999...')
  • 定義服務器地址server_address = ('127.0.0.1', 9999)?創建了一個包含 IP 地址(本地回環地址?127.0.0.1,用于在本地進行測試,只有本機上的程序能訪問)和端口號?9999?的元組,9999?作為服務器監聽的端口號,需確保該端口未被其他程序占用,否則綁定會失敗并拋出異常。
  • 綁定套接字到地址server_socket.bind(server_address)?方法將創建好的?server_socket?套接字綁定到指定的?server_address?上,使得服務器在該網絡端點上等待接收客戶端發送的數據報,一旦綁定成功,服務器就開始在這個端口監聽客戶端的數據發送。
  • 接收并處理客戶端消息
while True:data, client_address = server_socket.recvfrom(1024)if data:message = data.decode('utf-8')print(f'從客戶端收到消息: {message}')response = message.upper()server_socket.sendto(response.encode('utf-8'), client_address)print(f'已將處理后的消息發送回客戶端')
  • 循環接收客戶端數據報while True?構建了一個無限循環,讓服務器持續處于監聽狀態,能夠不斷接收不同客戶端發送的數據報,只要服務器運行,就會一直等待并處理新收到的數據。
  • 接收客戶端數據報及地址data, client_address = server_socket.recvfrom(1024)?調用是 UDP 服務器接收數據報的關鍵操作,它會阻塞程序執行,直到接收到客戶端發送的數據報為止,然后返回接收到的數據(字節形式)和客戶端的地址(包含客戶端的 IP 地址和端口號,以元組形式呈現)。參數?1024?表示最多接收?1024?字節的數據,可根據實際需求調整這個值。
  • 處理并返回數據報:如果接收到了數據(即?data?不為空),首先通過?message = data.decode('utf-8')?將接收到的字節數據按照 UTF-8 編碼格式解碼為字符串形式,賦值給?message?變量,并通過?print(f'從客戶端收到消息: {message}')?打印出客戶端發送的消息內容。接著,對消息進行處理(這里同樣是轉換為大寫形式),得到?response?變量,然后調用?server_socket.sendto(response.encode('utf-8'), client_address)?方法將處理后的消息?response?先編碼為字節流(使用 UTF-8 編碼),再根據接收到的客戶端地址?client_address?將數據報發送回客戶端,同時通過?print(f'已將處理后的消息發送回客戶端')?打印發送提示信息,告知服務器已成功回復客戶端。
客戶端代碼分析
  • 導入模塊與創建套接字對象
import socketclient_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • 導入模塊:和前面一樣,通過?import socket?導入?socket?模塊,以便后續使用套接字相關的功能來創建 UDP 套接字并進行通信操作。
  • 創建套接字對象client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)?創建了一個客戶端使用的基于 UDP 協議的數據報套接字對象?client_socket,使用?socket.AF_INET(IPv4 地址族)和?socket.SOCK_DGRAM(UDP 協議)參數,使其能夠與服務器端創建的 UDP 套接字進行相應的數據報通信。
  • 發送消息并接收回復
server_address = ('127.0.0.1', 9999)
message = "Hello, UDP Server!"
client_socket.sendto(message.encode('utf-8'), server_address)
print(f'已向服務器發送消息: {message}')
data, server_address = client_socket.recvfrom(1024)
if data:response = data.decode('utf-8')print(f'從服務器收到回復: {response}')
client_socket.close()
  • 定義服務器地址server_address = ('127.0.0.1', 9999)?定義了要發送數據報的目標服務器的 IP 地址和端口號,該地址必須與服務器端綁定并監聽的地址端口一致,這樣才能確保數據報能準確發送到服務器。
  • 發送消息給服務器:定義要發送的消息?message = "Hello, UDP Server!",然后通過?client_socket.sendto(message.encode('utf-8'), server_address)?調用將消息先編碼為字節流(采用 UTF-8 編碼格式),再根據指定的?server_address?將數據報發送給服務器,同時通過?print(f'已向服務器發送消息: {message}')?打印發送提示信息,方便查看發送情況。
  • 接收服務器回復:接著調用?client_socket.recvfrom(1024)?嘗試從服務器接收回復的數據報,最多接收?1024?字節的數據,當接收到數據報后(即?data?不為空),通過?response = data.decode('utf-8')?將接收到的字節數據按照 UTF-8 編碼格式解碼為字符串形式,賦值給?response?變量,并通過?print(f'從服務器收到回復: {response}')?打印出服務器回復的內容,實現與服務器之間的簡單消息交互。
  • 關閉套接字釋放資源:最后調用?client_socket.close()?方法關閉客戶端套接字,釋放相關資源,確保程序結束時資源被正確回收,避免資源浪費或潛在的資源泄漏問題。

結束語

希望以上對 “套接字是什么?為什么在服務器端與客戶端建立了套接字就可以通信了?” 這一問題的全方位解析,包括題目剖析、代碼實現與深入的代碼分析,能讓你順利敲開網絡編程中套接字這扇關鍵大門。套接字作為網絡通信的核心樞紐,無論是基于 TCP 協議構建如網頁瀏覽、文件傳輸這般穩定可靠的交互場景,還是利用 UDP 協議實現實時性強的游戲、視頻流傳輸等應用,它都展現出無可比擬的靈活性與強大功能,完美架起服務器與客戶端之間的信息橋梁。

在后續的編程探索之旅,倘若你立志投身網絡應用開發、分布式系統構建等前沿領域,對套接字扎實且深入的理解都將成為你披荊斬棘的有力武器。若在前行路上遭遇網絡編程的迷霧,或是渴望拓展更精妙的通信技巧,隨時回溯這些知識結晶,我也將一如既往地為你答疑解惑,伴你一路奮進,邁向編程新高峰!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65041.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65041.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65041.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2017年IMO幾何預選題第7題

凸四邊形 A B C D ABCD ABCD 有內切圓 I I I, △ D A B \triangle DAB △DAB, △ A B C \triangle ABC △ABC, △ B C D \triangle BCD △BCD, △ C D A \triangle CDA △CDA 的內心分別為 I a I_a Ia?, I b I_b Ib?, I c I_c Ic?, I d I_d Id?. △ A I b I d \…

RabbitMQ案例

1. 導入依賴 <!--AMQP依賴&#xff0c;包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency> 發送消息 注入RabbitTemplate Autowired RabbitT…

特殊數據類型的深度分析:JSON、數組和 HSTORE 的實用價值

title: 特殊數據類型的深度分析:JSON、數組和 HSTORE 的實用價值 date: 2025/1/4 updated: 2025/1/4 author: cmdragon excerpt: 隨著數據管理需求的多樣化,許多現代數據庫系統開始支持特殊數據類型,以滿足更多復雜應用場景的需求。在 PostgreSQL 中,JSON、數組和 HSTOR…

#滲透測試#漏洞挖掘#WAF分類及繞過思路

免責聲明 本教程僅為合法的教學目的而準備&#xff0c;嚴禁用于任何形式的違法犯罪活動及其他商業行為&#xff0c;在使用本教程前&#xff0c;您應確保該行為符合當地的法律法規&#xff0c;繼續閱讀即表示您需自行承擔所有操作的后果&#xff0c;如有異議&#xff0c;請立即停…

【Logstash02】企業級日志分析系統ELK之Logstash 輸入 Input 插件

Logstash 使用 Logstash 命令 官方文檔 https://www.elastic.co/guide/en/logstash/current/first-event.html #各種插件 https://www.elastic.co/guide/en/logstash/current/input-plugins.html https://www.elastic.co/guide/en/logstash/current/filter-plugins.html htt…

1.4 java反射機制 簡單的java反射機制實踐

這是一個項目用于學習反射 第一個demo是利用反射構建一個對象轉換為JSON 第二個demo是用于利用類的名字以及方法名就可以直接執行的實例 package com.zy.reflectiondemo.utils;import com.zy.reflectiondemo.annotation.JsonField;import java.lang.reflect.Field; import jav…

C#設計模式(行為型模式):觀察者模式

C#設計模式&#xff1a;觀察者模式&#xff0c;讓對象間通信更優雅 在軟件開發中&#xff0c;我們經常會遇到一個對象的狀態發生改變&#xff0c;其他對象需要自動更新或做出相應反應的場景。例如&#xff1a; GUI事件處理&#xff1a; 當用戶點擊按鈕時&#xff0c;按鈕需要…

【Vue】:解決動態更新 <video> 標簽 src 屬性后視頻未刷新的問題

問題描述 在 Vue.js 項目&#xff0c;當嘗試動態更新 <video> 標簽的 <source> 元素 src 屬性來切換視頻時&#xff0c;遇到了一個問題&#xff1a;即使 src 屬性已更改&#xff0c;瀏覽器仍顯示舊視頻。具體表現為用戶選擇新視頻后&#xff0c;視頻區域繼續顯示之…

BerOS 文件系統路徑歸一化問題及其 Python 實現

題目背景 本文將討論一道與操作系統路徑歸一化有關的問題&#xff0c;該問題來自 BerOS 文件系統 的設計。BerOS 是一個新型操作系統&#xff0c;其文件路徑系統允許路徑中的分隔符 / 重復出現。例如&#xff0c;以下路徑被視為等價的&#xff1a; /usr//local//nginx/sbin//…

Halcon 顯示異常

//For Halcon System HOperatorSet.SetSystem("clip_region", "false"); set_system( clip_region, false) *旋轉 hom_mat2d_identity (HomMat2DIdentity1) hom_mat2d_rotate (HomMat2DIdentity1, rad( 90), 0, 0, HomMat2DRotate) affine_trans_region …

window11 wsl mysql8 錯誤分析:1698 - Access denied for user ‘root‘@‘kong.mshome.net‘

&#x1f6a8; 錯誤分析&#xff1a;1698 - Access denied for user rootkong.mshome.net 這個錯誤是因為 MySQL 的 root 用戶 使用 auth_socket 插件進行身份驗證&#xff0c;而不是使用密碼。因此&#xff0c;當你嘗試從 遠程主機 連接時&#xff0c;MySQL 會拒絕訪問。 ? …

CentOS 7安裝Docker詳細教程

本文以 CentOS7.8 為例安裝 Docker 26.1.4 、Docker Compose、以及 Docker 鏡像倉庫。 安裝方式1&#xff1a;自動安裝(使用官方腳本) 使用官網一鍵安裝命令&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 或 使用國內 daocloud 一鍵安…

Java:緩存:LinkedHashMap實現Lru

文章目錄 Lru源碼分析 ??????????????LinkedHashMap維護一個LinkedHashMapEntry<K,V>的雙向鏈表對LinkedHashMap的增刪查操作,也會對鏈表進行相同的操作并改變鏈表的鏈接順序小結使用方法??????????????應用總結Lru Least Recently Used,…

通過代理用戶功能可以實現生產用戶的應用滾動修改密碼

Oracle通過代理用戶功能可以實現生產用戶的應用滾動修改密碼。 測試例子&#xff1a; 生產用戶為jyc密碼為jyc 現在要求修改jyc的密碼為abc&#xff0c;意味著所有應用都得停止同時修改。 此時可以考慮新建代理用戶proxy_jyc&#xff0c;密碼為jyc1&#xff08;實際修改建議…

Git 倉庫與文件管理筆記

Git 的三種倉庫概念 本地倉庫 (Local Repository) 位于本地 .git 文件夾中通過 git init 或 git clone 創建存儲完整的項目歷史和分支信息 遠程倉庫 (Remote Repository) 位于 GitHub、GitLab 等平臺服務器使用 git remote -v 查看所有遠程倉庫默認遠程倉庫名通常為 origin 工…

【人工智能數據科學與數據處理】——深入詳解人工智能數據科學與數據處理之數據可視化與數據庫技術

深入詳解人工智能數據科學與數據處理 在人工智能&#xff08;AI&#xff09;的數據科學與數據處理中&#xff0c;數據可視化與數據庫技術是兩項至關重要的技能。本文將深入探討數據可視化中的可視化技巧及其應用&#xff0c;以及關系型數據庫&#xff08;如MySQL、PostgreSQL&…

DES密碼的安全性分析(簡化版本)

DES仍是世界上使用最廣的&#xff08;DES發行后20年&#xff0c;互聯網的興起&#xff0c;人們開始覺得DES不安全了&#xff0c;但DES的實現成本也越來越低&#xff09; 宏觀分析&#xff1a; 密鑰空間方面&#xff1a; 密鑰長度&#xff1a;DES 算法使用 56 位的密鑰對數據…

Elasticsearch 文檔批處理 混合處理 批量操作

介紹 在 Elasticsearch 中&#xff0c;批量操作&#xff08;Bulk API&#xff09;允許你一次執行多個文檔操作&#xff08;如索引、更新、刪除&#xff09;以提高效率。批量操作對于大規模數據的插入、更新或刪除尤其有用&#xff0c;可以顯著提高處理速度。 批量操作通常是通…

計算機網絡原理(謝希仁第八版)第4章課后習題答案

第四章 網絡層 詳細計算機網絡&#xff08;謝希仁-第八版&#xff09;第四章習題全解_計算機網絡第八版謝希仁課后答案-CSDN博客 1.網絡層向上提供的服務有哪兩種&#xff1f;是比較其優缺點。網絡層向運輸層提供 “面向連接”虛電路&#xff08;Virtual Circuit&#xff09;服…

實現單例模式的五種方式

如何實現一個單例 1、構造器需要私有化 2、提供一個私有的靜態變量 3、暴露一個公共的獲取單例對象的接口 需要考慮的兩個問題 1、是否支持懶加載 2、是否線程安全 1、餓漢式 public class EagerSingleton {private static final EagerSingleton INSTANCE new EagerSi…