Python 中的套接字程式設計:初學者指南

Aditya Raj 2023年1月30日 2022年5月18日
  1. 在 Python 中如何實現 Socket 程式設計
  2. 如何在 Python 的 Socket 程式設計中建立伺服器
  3. 在 Python 中使用 UDP 協議進行套接字程式設計
  4. まとめ
Python 中的套接字程式設計:初學者指南

通常,當我們編寫程式時,我們不需要與其他程式或計算機進行通訊。

但是,我們可能需要與其他計算機通訊以建立具有伺服器-客戶端架構的信使或其他應用程式。為了建立這樣的應用程式,我們可以使用 Python 中的套接字程式設計。

本文將討論 Python 中套接字程式設計的基礎知識。我們還將使用 TCP 和 UDP 協議的套接字程式設計分別實現一個簡單的訊息應用程式。

Python 中的套接字是什麼

當兩個應用程式或程序互動時,它們使用指定的通訊通道。套接字是此類通訊通道的端點或入口點。

我們可以使用套接字在兩個程序之間、一個程序內或不同機器上的程序之間建立通訊通道。有不同型別的套接字,例如 TCP 套接字、UDP 套接字和 UNIX 域套接字。

在 Python 中如何實現 Socket 程式設計

Python 為我們提供了 socket 模組來實現套接字程式設計。socket 模組是標準 Python 庫的一部分,它提供了你可以在 Python 中建立套接字的所有函式和方法。

你不需要在你的機器中顯式下載 socket 模組,你可以使用如下 import 語句直接將其匯入你的程式中。

import socket

要實現套接字程式設計,我們需要建立兩個使用套接字進行通訊的程序。

其中一個程式用作伺服器,另一個程式用作客戶端。伺服器和客戶端都有不同的功能。因此,我們在建立伺服器和客戶端程序時使用不同的功能。

讓我們一一討論如何建立伺服器和客戶端程序。

如何在 Python 的 Socket 程式設計中建立伺服器

要建立伺服器,我們將首先建立一個套接字。為此,我們使用 socket() 方法。

建立一個套接字:socket() 方法

socket() 方法的語法如下。

socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM, proto=0, fileno=None)

這裡,

  • 引數 family 表示套接字所屬的地址族。預設情況下,它是 AF_INET,並使用 Internet 協議版本 4 (IPv4) 地址建立一個套接字。你可以使用其他地址系列,例如用於 UNIX 地址的 AF_UNIX 和用於 Internet 協議版本 6 (IPv6) 地址的 AF_INET6
  • 引數 type 表示套接字型別。預設情況下,它的值 SOCK_STREAM 表示套接字將遵循面向連線的 TCP 協議。你可以使用 SOCK_DGRAM 建立遵循 UDP 協議的資料包套接字。
  • 引數 proto 表示協議號,通常為 0。如果在引數族中使用地址族 AF_CAN,則協議號應為 CAN_RAW、CAN_BCM、CAN_ISOTP 或 CAN_J1939 之一。
  • 引數 fileno 包含預設值 None。如果我們在 fileno 中指定檔案描述符,引數 familytypeproto 的值會自動從檔案描述符中檢測。

建立套接字後,我們使用 bind() 方法將其繫結到地址和埠號。

將套接字繫結到地址:bind() 方法

使用 socket() 函式,在我們建立的套接字物件上呼叫 bind() 方法。

它需要一個包含套接字將繫結到的地址的元組。地址的格式可能會因你選擇的地址系列而異。我們將建立一個地址族為 AF_INET 的套接字。因此,地址將包含主機名和埠號。

bind() 方法的語法如下。

bind((hostname,port))

你可以明確指定主機名。如果你在本地機器上建立伺服器,你可以將主機名指定為 localhost127.0.0.1,這是 localhost 地址的預設值。

或者,你可以使用 gethostname() 方法獲取主機名。對於引數 port,你可以使用大於 1024 且小於 65535 的任何埠號。

將套接字繫結到地址後,伺服器會監聽客戶端的連線請求。為此,我們使用 listen() 方法。

監聽連線:listen() 方法

listen() 方法的語法如下。

listen(backlog)

這裡,引數 backlog 表示在拒絕新連線之前系統將允許的最大未接受連線數。

執行 listen() 方法後,伺服器準備好接受連線。

接受連線請求:accept() 方法

伺服器不斷地在無限迴圈中執行,並監聽客戶端請求以接受來自客戶端的連線。一旦找到客戶端請求,伺服器使用 accept() 方法接受請求。

accept() 方法返回一個元組 (client, address)。在這裡,client 表示我們用來傳送和接收訊息的新套接字物件。地址 是繫結客戶端套接字的位置。

與客戶端通訊:send()recv() 方法

接受連線後,伺服器可以與客戶端進行通訊。

我們使用 send() 方法向客戶端傳送訊息。send() 方法在 accept() 方法返回的 client 物件上呼叫。

我們使用 recv() 方法來接收訊息。recv() 方法,當在 client 物件上呼叫時,接受一個數字,表示它可以從連線中讀取的最大位元組數。執行後,返回從連線中讀取的資料。

所有操作完成後,我們需要關閉連線。為此,我們在 accept() 方法返回的 client 物件上呼叫 close() 方法。

在討論了建立伺服器所需的所有方法之後,讓我們建立一個伺服器程序。

import socket

mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)
print("Socket created.")
hostname = 'localhost'
portno = 9999
mySocket.bind((hostname, portno))
print("Socket bound to address {} and port number {}".format(hostname, portno))
mySocket.listen(5)
print("Listening for client.")
while True:
    client, client_addr = mySocket.accept()
    print("Connection established with client at address {}".format(client_addr))
    msg = client.recv(1024).decode()
    print("Message received from the client:")
    print(msg)
    print("Sending acknowledgment to the client.")
    msg_out = "Message received: {}. Thank you.".format(msg).encode()
    client.send(msg_out)
    print("Terminating the connection.")
    client.close()
    break

現在我們已經建立了一個伺服器,讓我們建立一個將與伺服器通訊的客戶端程序。

如何在 Socket 程式設計中建立客戶端

要建立客戶端,我們首先需要使用 socket() 方法建立一個套接字,就像我們在建立伺服器時所做的那樣。請記住,為客戶端套接字定義的協議應該與伺服器套接字相同。否則,程式將無法按預期執行。

建立套接字後,我們需要將其連線到伺服器。為此,我們將使用 connect() 方法。

連線到伺服器:connect() 方法

connect() 方法的語法如下。

connect((host, port))

這裡,引數 host 表示伺服器的地址。引數 port 表示建立伺服器套接字的埠號。在建立伺服器時,你應該為提供給 bind() 方法的主機和埠引數提供與輸入相同的值。

與伺服器通訊

連線到伺服器後,你可以使用 send()recv() 方法與伺服器通訊。最後,這將有助於使用 close() 方法從客戶端關閉連線。

以下是我們將用於建立客戶端程序的客戶端程式。

import socket

mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)
print("Socket created.")
hostname = 'localhost'
portno = 9999
mySocket.connect((hostname, portno))
print("Connection established with the server.")
msg = "Hi I am a TCP client created by Aditya."
print("Sending msg to the server:", msg)
mySocket.send(msg.encode())
msg_in = mySocket.recv(1024).decode()
print("Acknowledgment received from the server:")
print(msg_in)
print("Terminating the Connection.")
mySocket.close()

建立伺服器和客戶端後,讓我們現在執行程式。請記住,你應該同時執行客戶端程式和伺服器程式,以便兩個程序可以同時處於活動狀態並相互通訊。

帶有伺服器程式的終端中的輸出將如下所示:

Socket created.
Socket bound to address localhost and port number 9999
Listening for client.
Connection established with client at address ('127.0.0.1', 37958)
Message received from the client:
Hi I am a TCP client created by Aditya.
Sending acknowledgment to the client.
Terminating the connection.

帶有客戶端程式的終端中的輸出將如下所示:

Socket created.
Connection established with the server.
Sending msg to the server: Hi I am a TCP client created by Aditya.
Acknowledgment received from the server:
Message received: Hi I am a TCP client created by Aditya.. Thank you.
Terminating the Connection.

在 Python 中使用 UDP 協議進行套接字程式設計

在前面的部分中,我們在連線中建立了遵循 TCP 協議的套接字。在 TCP 協議中,客戶端和伺服器之間的連線在整個通訊過程中保持不變。

但是,在很多情況下,由於資源限制,我們無法保持客戶端和伺服器之間的穩定連線。因此,我們需要一個不需要穩定連線的通訊協議。為此,我們使用 UDP 協議。

在 Python 中如何使用 UDP 協議建立伺服器

要使用 UDP 協議建立連線,我們需要在實現伺服器時遵循以下步驟。

  • 在使用 socket() 方法建立伺服器套接字時,在型別引數的輸入中指定 SOCK_DGRAM
  • 使用 bind() 方法將套接字繫結到地址和埠號。
  • 由於我們不需要與客戶端建立連線,因此我們不使用 listen()accept() 方法來建立連線。我們可以直接開始與客戶溝通。
  • 要在 UDP 協議中接收訊息,我們使用 recvfrom() 方法。它將要讀取的位元組數作為輸入引數,並返回一個元組,其中包含資料和接收資料的地址。
  • 要在 UDP 協議中傳送訊息,我們使用 sendto() 方法。sendto() 方法將資料作為其第一個輸入引數,並將包含主機名和埠號的元組作為資料將傳送到的套接字地址。
  • 通訊後,你必須使用 close() 方法關閉套接字。

使用以下 Python 程式,你可以實現一個與 UDP 協議通訊的伺服器程序。

import socket

mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, proto=0, fileno=None)
print("Socket created.")
hostname = 'localhost'
portno = 9999
mySocket.bind((hostname, portno))
print("Socket bound to address {} and port number {}".format(hostname, portno))
while True:
    msg, client_addr = mySocket.recvfrom(1024)
    print("Message received from the client:")
    print(msg.decode())
    print("Sending acknowledgment to the client.")
    msg_out = "Message received: {}. Thank you.".format(msg).encode()
    mySocket.sendto(msg_out, client_addr)
    mySocket.close()
    break

在 Python 中如何使用 UDP 協議建立客戶端

要建立遵循 UDP 協議的客戶端程序,我們需要通過在 type 引數的輸入中指定 SOCK_DGRAM 來建立套接字,同時使用 socket() 方法建立伺服器套接字。我們不需要在這裡使用 connect() 方法,因為我們不必建立連線。

建立套接字後,我們可以使用 sendto()recvfrom() 方法直接開始與伺服器通訊。與伺服器通訊後,不要忘記使用 close() 方法關閉套接字。

使用以下 Python 程式,你可以實現與 UDP 協議通訊的客戶端程序。

import socket

mySocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM, proto=0, fileno=None)
print("Socket created.")
while True:
    msg = "Hi I am a UDP client created by Aditya."
    print("Sending msg to the server:", msg)
    mySocket.sendto(msg.encode(), ('localhost', 9999))
    msg_in = mySocket.recv(1024).decode()
    print("Acknowledgment received from the server:")
    print(msg_in)
    print("Terminating the Connection.")
    mySocket.close()
    break

同樣,要觀察輸出,你應該同時執行客戶端程式和伺服器程式,以便兩個程序可以同時處於活動狀態並且可以相互通訊。

帶有伺服器程式的終端中的輸出將如下所示:

Socket created.
Socket bound to address localhost and port number 9999
Message received from the client:
Hi I am a UDP client created by Aditya.
Sending acknowledgment to the client.

帶有客戶端程式的終端中的輸出將如下所示:

Socket created.
Sending msg to the server: Hi I am a UDP client created by Aditya.
Acknowledgment received from the server:
Message received: b'Hi I am a UDP client created by Aditya.'. Thank you.
Terminating the Connection.

まとめ

在本文中,我們討論了 Python 中的套接字程式設計。我們還分別使用 TCP 和 UDP 協議實現了客戶端和伺服器程式,以學習 Python 中套接字程式設計的基礎知識。

TCP 是面向連線的,因此是一種可靠的協議。使用 TCP 協議時,可以保證訊息從伺服器到達客戶端,反之亦然。在 UDP 中,不能保證訊息將被傳遞到所需的目的地。

另一方面,UDP 協議更快、更容易實現,而 TCP 協議速度較慢。另外,TCP 協議不能用於廣播,而我們可以使用 UDP 協議進行廣播。

根據可用資源和你的需要,你可以選擇任何協議來使用套接字實現客戶端-伺服器通訊。

相關文章 - Python Socket

相關文章 - Python Module