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
中指定檔案描述符,引數family
、type
和proto
的值會自動從檔案描述符中檢測。
建立套接字後,我們使用 bind()
方法將其繫結到地址和埠號。
將套接字繫結到地址:bind()
方法
使用 socket()
函式,在我們建立的套接字物件上呼叫 bind()
方法。
它需要一個包含套接字將繫結到的地址的元組。地址的格式可能會因你選擇的地址系列而異。我們將建立一個地址族為 AF_INET
的套接字。因此,地址將包含主機名和埠號。
bind()
方法的語法如下。
bind((hostname, port))
你可以明確指定主機名
。如果你在本地機器上建立伺服器,你可以將主機名指定為 localhost
或 127.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 協議進行廣播。
根據可用資源和你的需要,你可以選擇任何協議來使用套接字實現客戶端-伺服器通訊。
Aditya Raj is a highly skilled technical professional with a background in IT and business, holding an Integrated B.Tech (IT) and MBA (IT) from the Indian Institute of Information Technology Allahabad. With a solid foundation in data analytics, programming languages (C, Java, Python), and software environments, Aditya has excelled in various roles. He has significant experience as a Technical Content Writer for Python on multiple platforms and has interned in data analytics at Apollo Clinics. His projects demonstrate a keen interest in cutting-edge technology and problem-solving, showcasing his proficiency in areas like data mining and software development. Aditya's achievements include securing a top position in a project demonstration competition and gaining certifications in Python, SQL, and digital marketing fundamentals.
GitHub