使用 Select 的 TCP 和 UDP 伺服器


簡介

在伺服器-客戶端通訊方面,通常使用兩種協議:TCP 和 UDP。傳輸控制協議 (TCP) 是一種面向連線的協議,可確保網路裝置之間資料包的可靠交付。另一方面,使用者資料報協議 (UDP) 是一種無連線協議,它提供更快的數 據傳輸,但不能保證交付或順序。

在本文中,我們將探討如何使用 Python 程式語言構建使用這兩種協議的伺服器。我們還將討論在處理多個客戶端連線時使用 select() 函式。

設定伺服器

為 TCP 和 UDP 協議建立套接字

設定伺服器的第一步是為 TCP 和 UDP 協議建立套接字。套接字是促進不同裝置或程序透過網路進行通訊的端點。對於 TCP,我們使用面向流的協議,這要求我們建立 SOCK_STREAM 套接字。

另一方面,對於 UDP,我們使用面向資料報的協議並建立 SOCK_DGRAM 套接字。在 Python 中,我們可以使用內建的 `socket` 模組建立套接字。

以下程式碼片段建立了兩個套接字:一個用於 TCP 協議,一個用於 UDP 協議。

python 
# Import socket module import socket 
# Create TCP socket tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
# Create UDP socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  

將套接字繫結到特定 IP 地址和埠號

建立了兩個套接字後,我們需要將它們繫結到特定的 IP 地址和埠號。繫結是必要的,因為它允許客戶端在指定的地址和埠號上連線到我們的伺服器。

要在 Python 中繫結我們的套接字,我們在每個套接字上呼叫 `bind()` 方法,並使用兩個引數:- 我們的伺服器的 IP 地址。

在大多數情況下,這將是我們伺服器程式碼執行的機器的 IP 地址。- 我們想要與我們的伺服器關聯的埠號。

python # Bind TCP Socket 
tcp_socket.bind(('localhost', 5000)) # Bind UDP Socket 
udp_socket.bind(('localhost', 6000))  

在本例中,我們將 TCP 套接字繫結到埠 5000,同時將 UDP 套接字繫結到本地主機上的埠 6000。

偵聽傳入連線

成功將我們的套接字繫結到特定的 IP 地址和埠號後,我們需要將其設定為偵聽來自客戶端的傳入連線。換句話說,伺服器現在正在等待客戶端連線。對於 TCP 協議,我們對 TCP 套接字使用 `listen()` 方法。

此方法接受一個引數- 最大排隊連線數- 並開始偵聽傳入連線。

python 
# Listen for incoming TCP connections tcp_socket.listen(5)  

另一方面,由於 UDP 是無連線協議,因此無需設定偵聽模式。我們可以開始接收從傳送資訊到我們繫結套接字的任何客戶端傳送的資料。

python # Receive data from UDP clients 
while True: data, addr = udp_socket.recvfrom(1024) 

透過這些步驟,我們已成功使用 TCP 和 UDP 套接字設定了伺服器。我們的伺服器現在已準備好接受傳入的客戶端請求並相應地處理它們。

使用 Select() 處理連線

解釋 select() 函式及其引數

select() 函式是一個強大的工具,可以監視多個套接字以獲取傳入資料或連線。它非常高效且可擴充套件,使其成為同時處理大量客戶端的理想選擇。使用 select() 函式的基本語法如下

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);  

這裡,nfds 代表“任何集合中編號最高的檔案描述符加 1”。檔案描述符集由 readfds、writefds 和 exceptfds 表示。

使用 Select() 監視多個套接字以獲取傳入資料或連線

要使用 select(),首先建立一個要使用 FD_ZERO 和 FD_SET 函式監視的檔案描述符集。然後將這些集合作為引數傳遞給 select() 的呼叫。一旦呼叫,select() 將阻塞,直到在指定的一個或多個套接字上發生活動。

Select() 支援阻塞和非阻塞模式,具體取決於它的配置方式。

使用 Accept() 處理新連線

當一個新的客戶端嘗試透過我們之前建立的 TCP/UDP 協議連線到我們的伺服器套接字並且在埠 80(或分配的任何其他埠號)上沒有現有的連線時,我們的伺服器需要接受此新連線。這可以透過使用 accept() 來完成。

當在偵聽套接字上呼叫且沒有等待的活動連線時,此函式將阻塞,直到檢測到新的傳入連線。

TCP 伺服器實現

與客戶端建立面向連線的通訊

TCP 是一種面向連線的協議,它在伺服器和客戶端之間建立可靠的通訊通道。要建立這樣的連線,我們首先需要使用 socket() 系統呼叫建立 TCP 套接字,然後使用 bind() 將其繫結到地址。

之後,我們使用 listen() 使套接字偵聽傳入連線。一旦客戶端請求連線,accept() 用於接受連線請求並返回一個新的套接字描述符,該描述符將用於與該客戶端進行進一步通訊。

同時處理多個客戶端請求

同時處理多個客戶端請求是任何伺服器實現中最重要的方面之一。單執行緒伺服器一次只能服務一個客戶端,這限制了其可擴充套件性和效能。為了在我們的 TCP 伺服器實現中同時處理多個客戶端,我們可以使用多執行緒或多程序技術。

在多執行緒方法中,每個新連線都透過建立一個新執行緒來處理,該執行緒處理與該特定客戶端的所有通訊。這允許在一個程序中同時為多個客戶端提供服務。

另一方面,在多程序方法中,每個新連線都透過建立一個新程序來處理,該程序處理與該特定客戶端的所有通訊。這允許在多個程序中同時為多個客戶端提供服務。

UDP 伺服器實現

與客戶端建立無連線通訊

在 UDP 伺服器中,通訊是無連線的。這意味著伺服器不會與其客戶端建立持久連線。相反,每個客戶端都會發送單獨的資料報,伺服器會獨立處理這些資料報。

要設定 UDP 伺服器,我們首先建立並繫結一個套接字到 IP 地址和埠號。然後,我們偵聽來自多個客戶端的傳入資料報。

同時處理來自多個客戶端的資料報

在 UDP 伺服器中使用 select() 函式的一個關鍵優勢是它允許同時處理來自多個客戶端的資料報。當資料報到達伺服器時,select() 返回與該客戶端連線關聯的套接字描述符。然後,伺服器可以使用 sendto() 函式處理資料並將響應傳送回同一客戶端。

實施錯誤處理機制

與 TCP 伺服器一樣,錯誤處理機制在 UDP 伺服器中至關重要,以確保與客戶端進行高效且可靠的通訊。UDP 通訊中一個常見的問題是由於網路擁塞或其他因素導致的資料包丟失。

為了緩解此問題,我們可以實現諸如校驗和和確認訊息之類的技術來檢測和恢復丟失的資料包。另一個潛在的問題是當多個客戶端同時傳送過多資料報時發生緩衝區溢位,導致伺服器緩衝區溢位並丟失資料。

結論

在本文中,我們討論了 TCP 和 UDP 協議以及使用這些協議設定伺服器的過程。我們還探討了 select() 函式及其在監視多個套接字以獲取傳入資料或連線中的用法。

此外,我們還討論了 TCP 和 UDP 伺服器的實現及其各自的優點。select() 函式是一個強大的工具,它允許在面向連線 (TCP) 和無連線 (UDP) 通訊中高效地處理多個客戶端連線。

更新時間: 2023-07-11

727 次瀏覽

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告