socketserver 模塊簡介

来源:https://www.cnblogs.com/caesar-id/archive/2019/12/25/12094529.html
-Advertisement-
Play Games

一、socketserver模塊簡介 socketserver模塊簡化了網路編程,模塊下有五個服務類:BaseServer、TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer 。這五個類的關係如下: + + | BaseServer | + ...


一、socketserver模塊簡介

socketserver模塊簡化了網路編程,模塊下有五個服務類:BaseServer、TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer 。這五個類的關係如下:

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |

1、四個基本的同步伺服器類簡介: 

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True):TCP數據流伺服器
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True):UDP數據報伺服器

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True):僅限於Unix系統的,Unix套接字流
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True):僅限於Unix系統的,Unix數據報

他們的參數意義相同,如下:

server_address:伺服器的地址,他應該是一個元組包含地址和埠如:('192.168.1.1',9000),

RequestHandlerClass:我們自定義的類,類中必須重寫handle()方法。用於處理所有socket請求。

bind_and_activate:如果為True,將自動調用server_bind()和server_activate()。

 這四個類運行時只能處理一個請求,也就是一個服務端只能對應一個客戶端,這對於我們將來要編寫的FTP伺服器可能不適用,因為我們希望一個服務能處理多個客戶端,下麵我們來看socketserver為我們提供的兩個處理非同步的類。

2、非同步伺服器類簡介

class socketserver.ForkingMixIn:啟用多進程
class socketserver.ThreadingMixIn:啟用多線程

創建非同步服務的方法非常簡單,下麵創建一個簡單的TCP框架為例:

import socketserver

class MyTCPServer(socketserver.BaseRequestHandler):  # 自定義TCP服務類,必須繼承socketserver.BaseRequestHandler
    def handle(self):  # 重寫此類,
        '''
        我們要處理的任務
        :return: 
        '''
        self.fun()  # 執行我們的任務
    def fun(self):
        print('Hello World')

class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): # 類名隨便起,必須要繼承這兩個類
    pass
if __name__ == "__main__":
    ip_port = ("127.0.0.1", 9000)  # TCP伺服器的地址和埠

    with socketserver.ThreadingTCPServer(ip_port, MyTCPServer) as server:
        server.serve_forever()  # 開啟TCP服務

這樣我們就簡單的創建了一個非同步TCP伺服器框架,其實ThreadingTCPServer這個類我們不用自己創建,因為socketserver已經為我們創建好了,如下:

class ForkingUDPServer(ForkingMixIn, UDPServer): pass  # 多進程UDP伺服器
class ForkingTCPServer(ForkingMixIn, TCPServer): pass  # 多進程TCP伺服器
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass  # 多線程UDP伺服器
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass  # 多線程TCP伺服器

如果覺得socketserver提供的這四個類不是你想要的,那麼你就可以像上面那樣自己定義,上面都是服務類,通過服務類實例化對象,但是目前還不知道對象擁有哪些方法,因為這些服務類都是繼承的BaseServer類,所以方法都在BaseServer類中,有些方法只是定義了介面,在子類中實現的。

3、服務對象常用方法

class socketserver.BaseServer(server_address, RequestHandlerClass):

fileno():返回伺服器正在監聽的套接字的文件描述符(int類型的數字)。此函數最常傳遞給選擇器,以允許監視同一進程中的多個伺服器。

handle_request():處理單個請求。此函數按順序調用以下方法:get_request()、verify_request()和process_request()。如果handler類的用戶提供的handle()方法引發異常,則將調用伺服器的handle_error()方法。如果在超時秒內未收到請求,將調用handle_timeout(),並返回handle_request()。

serve_forever(poll_interval=0.5):定時任務,通常是在一個線程中,每poll_interval秒輪詢一次,直到調用shutdown停止。

service_actions():該函數被serve_forever定時函數重覆調用,這個方法我們可以繼承BaseServer,然後重寫此方法。

shutdown():此方法用於停止serve_forever()定時任務。

socket:socket對象。

socket_type:socket套接字類型,TCP,UDP等。

allow_reuse_address:伺服器是否允許地址的重用。預設為false ,並且可在子類中更改。

address_family:設置socket套接字家族。

server_address:值是一個元組,socket伺服器地址和監聽的埠。

server_activate():伺服器將處於監聽狀態,該函數可被重寫,其實他的內部就是self.socket.listen(self.request_queue_size)。

server_bind():將socket綁定到地址上,可以被重寫。

get_request():此方法的前提是必須接收到來自套接字的請求,返回一個元組(與客戶端通信的新套接字對象,客戶端地址)。其實該方法就是將self.socket.accept()的結果返回。

server_close():關閉服務(關閉socket),此方法可被重寫。

RequestHandlerClass:值是類名,這個類是我們定義的用於創建實例處理我們的請求,如上面TCP非同步框架中的MyTCPServer,這個類就是RequestHandlerClass的值。

request_queue_size:請求隊列的大小。如果處理單個請求需要很長時間,在伺服器繁忙時會將請求放到隊列中,當請求數達到request_queue_size的值時。來自客戶端的進一步請求將得到一個“拒絕連接”錯誤。預設值通常是5,但是這個值可以被子類覆蓋。

finish_request(request, client_address):此方法會實例化RequestHandlerClass並調用它的handle()方法來實際處理請求。

process_request(request,client_address):調用finish_request()來創建RequestHandlerClass的一個實例。我們可以自己創建線程池或進程池來調用這個函數,實現伺服器處理多個請求的問題,ForkingMixIn和ThreadingMixIn類就是這樣做的。

handle_error(request,client_address):如果RequestHandlerClass實例的handle()方法引發異常,則調用此函數。預設操作是將回溯列印到標準錯誤,並繼續處理其他請求。在版本3.6中更改:現在僅調用從Exception類派生的異常。

timeout:超時時間(以秒為單位),如果是None,會一直阻塞。如果設置了timeout,handle_request()在超時期間沒有收到傳入請求,則調用handle_timeout()方法。

handle_timeout():當timeout屬性被設置為None以外的值,並且超時周期已經過去而沒有收到任何請求時,將調用此函數。多進程 伺服器的預設操作是收集已退出的任何子進程的狀態,而線上程伺服器中,此方法不執行任何操作。

verify_request(request,client_address):返回一個布爾值;如果值為真,請求將被處理,如果值為假,請求將被拒絕。可以重寫此函數以實現伺服器的訪問控制。預設實現只是一句return True。

上面這些都是服務對象的方法,下麵來介紹處理socket請求類BaseRequestHandler。

4、客戶端請求處理類BaseRequestHandler

class socketserver.BaseRequestHandler:

這是所有socket請求處理程式的基類。它只定義了介面,而沒有實現,如果想要使用介面,我們首先繼承BaseRequestHandler,然後在子類中重寫這些方法。每個socket請求處理程式子類必須重寫handle()方法,因為該方法是用於處理所有socket請求。該類的方法如下:

setup():在handle()方法之前調用,執行初始化操作。 預設不執行任何操作,我們可以重寫此方法來實現程式的初始化。

handle():所有socket請求任務都是在這個函數內部完成的,我們在子類中必須重寫此方法,並處理socket請求,因為預設基類中的handle()的實現不執行任何操作。

finish():在handle()方法之後調用以執行清理操作。預設實現不執行任何操作。如果setup()引發異常,則不會調用此函數。

雖然上面的介面都只是定義而沒有實現,但是它的實例屬性還是很有用的;

self.request;客戶端和服務端的連接對象,用於發送數據,接收數據。 

self.client_address:socket客戶端地址 。

self.server:socket服務端信息。

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler
這兩個類是socketserver繼承BaseRequestHandler後重寫了setup(),finish(),實現了對讀,寫緩衝區的設置,有興趣的可以看看源碼。

5、官方示例:

TCP同步服務示例:

# 服務端
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):  # 自定義類,繼承BaseRequestHandler,處理socket請求

    def handle(self):  # socket客戶端請求
        self.data = self.request.recv(1024).strip()  # 接收socket客戶端發來的數據
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())  # 將數據大寫後發給客戶端
'''
class MyTCPHandler(socketserver.StreamRequestHandler):  
    # 自定義類,功能與上面的一樣,只不過是繼承StreamRequestHandler
    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())
'''

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()  # 啟用TCP伺服器
# 列印內容如下
127.0.0.1 wrote:
b'Hello World'

# 客戶端
import socket
import sys

HOST, PORT = "localhost", 9999
data = "Hello World"

# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))

    # Receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))

# 列印內容如下
Sent:     Hello World
Received: HELLO WORLD

UDP服務示例:

# 服務端
import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()
# 列印內容如下
127.0.0.1 wrote:
b'Hello World'

# 客戶端
import socket
import sys

HOST, PORT = "localhost", 9999
data = "Hello World"

# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))
# 列印內容如下
Sent:     Hello World
Received: HELLO WORLD

 TCP服務非同步示例:

import socket
import threading
import socketserver

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):  # 自定義socket請求處理類
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):  # 自定義線程類處理多個請求
    pass

def client(ip, port, message):
    '''
    socket客戶端
    :param ip: 服務段的IP地址
    :param port: 服務端的埠
    :param message: 給服務端發送的消息
    :return:
    '''
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":

    HOST, PORT = "localhost", 0  # 埠是0隨機獲取一個未被使用的埠
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    with server:
        ip, port = server.server_address  # 獲取服務端的IP地址和埠號
        server_thread = threading.Thread(target=server.serve_forever) # 創建線程對象
        server_thread.daemon = True  # 守護線程
        server_thread.start()  # 開啟線程,線上程中開啟TCP伺服器
        print("Server loop running in thread:", server_thread.name)
        # 模擬三個socket客戶端連接TCP伺服器
        client(ip, port, "Hello World 1")
        client(ip, port, "Hello World 2")
        client(ip, port, "Hello World 3")

        server.shutdown()
# 列印內容如下:
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

 

參考文檔:https://docs.python.org/3/library/socketserver.html?highlight=socketserver#module-socketserver



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 <input type="date" id="date_info" placeholder="請輸入時間"/> 2 3 ...... 4 5 <script> 6 $(document).ready(function () { 7 var time = new Date(); 8 var day ...
  • 1.側邊欄划出一個信息框 通常滑鼠浮動側邊欄都會划出一個信息框,每個信息框距離側邊欄的距離是相等的,所以處理此問題的方法就是 量取信息框距離側邊欄的距離,信息框設置絕對定位,父元素設置相對定位之後,信息框就會相對於父元素的相對定位進行絕對定位,取父元素的width:100%,然後加上距離信息框距離側 ...
  • 對於更簡單的Ajax調用,XMLHttpRequest是低級別的,更複雜的,並且你需要封裝函數。不幸的是,一旦你開始考慮超時,中止調用和錯誤捕獲的複雜性,Fetch也會如此。 ...
  • 首先用一個例子指出來constructor存在形式。 由上面的代碼我們總結出結論1:上面的代碼在控制台可以看出constructor是指向構造器Fruit的引用。 這個地方就有點奇怪了。這個constructor到底指向的是那個實例的構造器? 根據上面的代碼總結出結論2:constructor指向的 ...
  • 導讀 篇幅較長,乾貨十足,閱讀需要花點時間,全部手打出來的字,難免出現錯別字,敬請諒解。珍惜原創,轉載請註明出處,謝謝~! NoSql介紹與Redis介紹 什麼是Redis? Redis是用C語言開發的一個開源的高性能鍵值對(key-value)記憶體資料庫。 它提供五種數據類型來存儲值:字元串類型、 ...
  • 2019/12/24 | 在校大二上學期 | 太原科技大學 初學java後,我們會發現java難點不在於Java語法難學,而是把我們掛在瞭如何設計類的“弔繩”上了。這恰恰也是小白與入職多年的程式員的的差距之一。所以今天來分享一下我的觀點: 那麼如何設計類呢?有沒有遵循的原則可言呢?答案呢,就是下麵的 ...
  • 什麼是NServiceBus?NServiceBus 是一個用於構建企業級 .NET系統的開源通訊框架。它在消息發佈/訂閱支持、工作流集成和高度可擴展性等方面表現優異,因此是很多分散式系統基礎平臺的理想選擇。它能夠幫助開發人員在搭建企業.NET系統時避免很多典型的常見問題。同時,該框架也提供了一些可 ...
  • WEB API的應用場景非常豐富,例如:將已有系統的功能或數據開放給合作伙伴或生態圈;對外發佈可嵌入到其他網頁的微件;構建前後端分離的WEB應用;開發跨不同終端的移動應用;集成公司內部不同系統等等。在上述場景里,你可能是WEB API的使用者,也可能是設計者,但你知道如何評判WEB API的優劣嗎? ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...