欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python內(nèi)置HTTP Server如何實(shí)現(xiàn)及原理解析

 更新時(shí)間:2023年11月26日 16:59:04   作者:王稀飯  
這篇文章主要為大家介紹了python內(nèi)置HTTP Server如何實(shí)現(xiàn)及原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

應(yīng)用案例

from http.server import HTTPServer, BaseHTTPRequestHandler  
IP = '127.0.0.1'  
PORT = 8000  
class Handler(BaseHTTPRequestHandler):  
    def do_GET(self):  
        self.send_response(200)  
        self.send_header('Content-type', 'text/html')  
        self.end_headers()  
        message = "Hello, World!"  
        self.wfile.write(bytes(message, "utf8"))  
with HTTPServer((IP, PORT), Handler) as httpd:  
    print("serving at port", PORT)  
    httpd.serve_forever()

以上是使用內(nèi)置模塊 http.server 實(shí)現(xiàn)的一個(gè)最簡(jiǎn)單的 http 服務(wù)器,能處理 http GET 請(qǐng)求。

python 內(nèi)置的 http server 主要集中在兩個(gè)代碼文件上,分別是 socketserver.py 和 http/server.pysocketserver.py 提供 socket 通信能力的 Server 封裝并預(yù)留了用戶自定義請(qǐng)求處理的接口;http/server.py 基于前者做進(jìn)一步封裝,用得比較多的是 HTTP 的封裝。

從開頭的例子出發(fā)閱讀代碼(python 3.10.1),大致梳理出以下代碼結(jié)構(gòu),圖畫得很隨意無(wú)規(guī)范可言,只是為了更具象化解釋。

問(wèn)題一:實(shí)現(xiàn)一個(gè) HTTP 服務(wù)器大致需要什么要素

先看圖 1,左邊 BaseServer 一列是類,從上往下是父類到子類;右邊 server_forever() 一列是方法,從上往下是逐步深入的調(diào)用鏈。

                從父類到子類                                 主線流程

            +----------------+                      +------------------+
            |                |                      |                  |
            |   BaseServer   +--------------------->| serve_forever()  |
            |                |                      |                  |
            +--------+-------+                      +--------=+--------+
                     |                                        |
                     |                                        |
                     |                                        |
                     V                                        V
            +----------------+                  +----------------------------+
            |                |                  |                            |
            |   TCPServer    |                  | _handle_request_noblock()  |
            |                |                  |                            |
            +--------+-------+                  +-------------+--------------+
                     |                                        |
         +-----------+------------+                           |
         |                        |                           |
         V                        V                           V
+----------------+       +----------------+         +------------------+
|                |       |                |         |                  |
|   HTTPServer   |       |   UDPServer    |         | process_request()|
|                |       |                |         |                  |
+----------------+       +----------------+         +---------+--------+
                                                              |
                                                              |
                                                              |
                                                              V
                                                    +------------------+
                                                    |                  |
                                                    | finish_request() |
                                                    |                  |
                                                    +------------------+

圖 1

例子中使用了 HTTPServer 這個(gè)類,字面意思,這個(gè)類就是一個(gè) HTTP 服務(wù)器,順著繼承鏈看到 HTTPServer 是 TCPServer 的子類,符合 HTTP 報(bào)文是基于 TCP 協(xié)議傳輸?shù)恼J(rèn)知,HTTPServer 類其實(shí)沒(méi)什么內(nèi)容,代碼如下:

class HTTPServer(socketserver.TCPServer):  
    allow_reuse_address = 1 # Seems to make sense in testing environment  
     def server_bind(self):  
         """Override server_bind to store the server name."""  
         socketserver.TCPServer.server_bind(self)
         host, port = self.server_address[:2]
         self.server_name = socket.getfqdn(host)
         self.server_port = port

TCPServer 的源碼實(shí)現(xiàn)得益于父類的預(yù)留接口,只需要 TCP socket 走一遍 bindlisten、acceptclose 流程(子類 UDPServer 同理)。

重點(diǎn)關(guān)注 BaseServer,這里是網(wǎng)絡(luò)請(qǐng)求處理核心流程的實(shí)現(xiàn),文章最開頭的例子中 serve_forever() 這個(gè)入口方法就是在此類被實(shí)現(xiàn),我在源碼上加了些簡(jiǎn)單的注釋:

def serve_forever(self, poll_interval=0.5):
    """Handle one request at a time until shutdown.
    Polls for shutdown every poll_interval seconds. Ignores
    self.timeout. If you need to do periodic tasks, do them in
    another thread. 
    """
    self.__is_shut_down.clear()  
    try:  
        # XXX: Consider using another file descriptor or connecting to the  
        # socket to wake this up instead of polling. Polling reduces our 
        # responsiveness to a shutdown request and wastes cpu at all other 
        # times. with _ServerSelector() as selector:  
        selector.register(self, selectors.EVENT_READ)  # 注冊(cè)Server描述符并監(jiān)聽I(yíng)/O讀事件
        while not self.__shutdown_request:  
            ready = selector.select(poll_interval)  # 超時(shí)時(shí)長(zhǎng)poll_interval避免長(zhǎng)時(shí)間阻塞,在while循環(huán)下實(shí)現(xiàn)輪詢
            # bpo-35017: shutdown() called during select(), exit immediately.  
            if self.__shutdown_request:  
                break  
            if ready:  
                self._handle_request_noblock()  # 請(qǐng)求過(guò)來(lái),I/O讀事件準(zhǔn)備好,開始處理請(qǐng)求
            self.service_actions()  
     finally:  
         self.__shutdown_request = False  
         self.__is_shut_down.set()

從 _handle_request_noblock() 中看到,一個(gè)網(wǎng)絡(luò)請(qǐng)求的處理流程無(wú)非就是 verify_request()、process_request()shoutdown_request() 加上些許異常處理邏輯,比較簡(jiǎn)明。在 finish_request() 中出現(xiàn) RequestHandlerClass 的類對(duì)象創(chuàng)建,這里其實(shí)就是用戶自定義的 RequestHandler(在 BaseServer 的 __int__() 中被初始化)。源碼如下,較好理解:

def _handle_request_noblock(self):  
    """Handle one request, without blocking.
    I assume that selector.select() has returned that the socket is
    readable before this function was called, so there should be no risk of
    blocking in get_request(). 
    """
    try:  
        request, client_address = self.get_request()  
    except OSError:  
        return  
    if self.verify_request(request, client_address):  # 從這里開始就是網(wǎng)絡(luò)請(qǐng)求的處理流程
        try:  
            self.process_request(request, client_address)  
        except Exception:  
            self.handle_error(request, client_address)  
            self.shutdown_request(request)  
        except:  
            self.shutdown_request(request)  
            raise  
    else:  
        self.shutdown_request(request)
def process_request(self, request, client_address):  
    """Call finish_request.  
    Overridden by ForkingMixIn and ThreadingMixIn.  
    """
    self.finish_request(request, client_address)  
    self.shutdown_request(request)
def finish_request(self, request, client_address):  
    """Finish one request by instantiating RequestHandlerClass."""  
    self.RequestHandlerClass(request, client_address, self)
def shutdown_request(self, request):  
    """Called to shutdown and close an individual request."""  
    self.close_request(request)

小結(jié):要實(shí)現(xiàn)一個(gè) HTTP 服務(wù)器,需要包含 TCP socket 實(shí)現(xiàn),網(wǎng)絡(luò)請(qǐng)求流程大致抽象為 verify_request()、process_request()shoutdown_request() 。如果考慮支持用戶自定義請(qǐng)求處理,還需要預(yù)留接口提供擴(kuò)展性。當(dāng)然如何要支持處理 HTTP 協(xié)議,還需要具備解析 HTTP 報(bào)文的能力,下文繼續(xù)探討。

問(wèn)題二:python 內(nèi)置的 HTTP Server 是怎么實(shí)現(xiàn)的

前文介紹了內(nèi)置一個(gè)網(wǎng)絡(luò)請(qǐng)求的處理流程(等價(jià)于 HTTP Server 的運(yùn)行流程),一定程度上解釋了本節(jié)的問(wèn)題,但欠缺一點(diǎn)細(xì)節(jié),沒(méi)有體現(xiàn) HTTP 報(bào)文的解析邏輯在哪里實(shí)現(xiàn)。其實(shí)內(nèi)置的 HTTP Server 的把 HTTP 協(xié)議解析的工作解耦出去,單獨(dú)做成 BaseHTTPRequestHandler 類,這樣允許用戶自行實(shí)現(xiàn)任意應(yīng)用層的協(xié)議解析工作,參考下面圖 2:

                              +----------------------+        +----------------+
                              |                      |        |                |
                              |  BaseRequestHandler  +------->|   __init__()   |
                              |                      |        |                |
                              +-----------+----------+        +----------------+
                                          |
                                          |
                                          |                   +----------------+
                                          |                   |                |
                                          V              +--->|     setup()    |
                              +----------------------+   |    |                |
                              |                      |   |    +----------------+
                              | StreamRequestHandler +---+
                              |                      |   |
                              +-----------+----------+   |    +----------------+
                                          |              |    |                |
                                          |              +---->    finish()    |
                                          V                   |                |
+------------------------+    +----------------------+        +----------------+
|                        |    |                      |
|SimpleHTTPRequestHandler|<---+BaseHTTPRequestHandler|
|                        |    |                      |
+------------------------+    +-----------+----------+
                                          |
                                          |
                                          |
                                          V
                                +------------------+
                                |                  |
                                |     handler()    |
                                |                  |
                                +---------+--------+          +----------------+
                                          |                   |                |
                                          |              +--->| parse_request()|
                                          |              |    |                |
                                          V              |    +----------------+
                              +----------------------+   |
                              |                      |   |
                              | handler_one_request()+---+
                              |                      |   |    +----------------+
                              +----------------------+   |    |                |
                                                         +--->|    do_XXX()    |
                                                              |                |
                                                              +----------------+

圖 2

圖 2 中,但凡帶括號(hào)的都是方法,不帶括號(hào)的是類,從上往下也是父類到子類。本著代碼最大化復(fù)用的原則,父類 BaseRequestHandler 的 __init__() 中將工作流程確定下來(lái),分別是 setup()、handler()、finish() 的先后調(diào)用順序。setup() 和 finish() 在子類 StreamRequestHandler 被實(shí)現(xiàn),最后在 BaseHTTPRequestHandler 類中實(shí)現(xiàn) HTTP 協(xié)議解析功能,以及用 HTTP method 來(lái)決定調(diào)用哪個(gè)用戶自定義的 do_XXX() 方法,如 do_GET()、do_POST() 等。代碼如下:

class BaseRequestHandler:  
    """Base class for request handler classes.  
    ......
    """  
    def __init__(self, request, client_address, server):  
        self.request = request  
        self.client_address = client_address  
        self.server = server  
        self.setup()  
        try:  
            self.handle()  
        finally:  
            self.finish()  
    def setup(self):  
        pass  
    def handle(self):  
        pass  
    def finish(self):  
        pass
class StreamRequestHandler(BaseRequestHandler):  
    """Define self.rfile and self.wfile for stream sockets."""  
    # 省略代碼
    def setup(self):  
        # 設(shè)置鏈接超時(shí)時(shí)長(zhǎng)、nagle算法、讀寫緩沖區(qū)
        self.connection = self.request  
        if self.timeout is not None:  
            self.connection.settimeout(self.timeout)  
        if self.disable_nagle_algorithm:  
            self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)  
        self.rfile = self.connection.makefile('rb', self.rbufsize)  
        if self.wbufsize == 0:  
            self.wfile = _SocketWriter(self.connection)  
        else:  
            self.wfile = self.connection.makefile('wb', self.wbufsize)  
    def finish(self):  
        if not self.wfile.closed:  
            try:  
                self.wfile.flush()  
            except socket.error:  
                # A final socket error may have occurred here, such as  
                # the local error ECONNABORTED. 
                pass  
        self.wfile.close()  
        self.rfile.close()

HTTP 協(xié)議解析關(guān)注 parse_request() 方法,由于代碼較多不單獨(dú)貼過(guò)來(lái),思路如下:

  • 解析 HTTP 協(xié)議版本號(hào),確定版本解析是否支持(1.1 <= version < 2.0)
  • 獲取 HTTP method
  • 解析 HTTP header 解析完 HTTP 協(xié)議后,根據(jù)所獲取的 HTTP method,調(diào)用用戶自定義的對(duì)應(yīng)方法,至此結(jié)束。

總結(jié)

python 內(nèi)置的 HTTP Server 實(shí)現(xiàn)比較簡(jiǎn)潔,功能相對(duì)簡(jiǎn)單。如果要自行從零實(shí)現(xiàn)一個(gè) HTTP Server,設(shè)計(jì)上參考 python 的實(shí)現(xiàn),應(yīng)該具備以下要素:

  • TCP socket 通信
  • HTTP 協(xié)議的報(bào)文解析
  • 用戶自定義的 RequestHandler 調(diào)用(設(shè)計(jì)上需要引入拓展)

相關(guān)文章

  • Python+Selenium實(shí)現(xiàn)短視頻熱點(diǎn)爬取

    Python+Selenium實(shí)現(xiàn)短視頻熱點(diǎn)爬取

    隨著短視頻的大火,不僅可以給人們帶來(lái)娛樂(lè),還有熱點(diǎn)新聞時(shí)事以及各種知識(shí),刷短視頻也逐漸成為了日常生活的一部分。本文將通過(guò)Pyhton依托Selenium來(lái)爬取短視頻熱點(diǎn),需要的可以參考一下
    2022-04-04
  • python判斷字符串編碼的簡(jiǎn)單實(shí)現(xiàn)方法(使用chardet)

    python判斷字符串編碼的簡(jiǎn)單實(shí)現(xiàn)方法(使用chardet)

    這篇文章主要介紹了python判斷字符串編碼的簡(jiǎn)單實(shí)現(xiàn)方法,涉及chardet模塊的安裝與簡(jiǎn)單使用方法,需要的朋友可以參考下
    2016-07-07
  • python實(shí)現(xiàn)祝福彈窗效果

    python實(shí)現(xiàn)祝福彈窗效果

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)祝福彈窗效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • LyScript實(shí)現(xiàn)Hook改寫MessageBox的方法詳解

    LyScript實(shí)現(xiàn)Hook改寫MessageBox的方法詳解

    LyScript可實(shí)現(xiàn)自定義匯編指令的替換功能。用戶可自行編寫匯編指令,將程序中特定的通用函數(shù)進(jìn)行功能改寫與轉(zhuǎn)向操作,此功能原理是簡(jiǎn)單的Hook操作。本文將詳細(xì)介紹Hook改寫MessageBox的方法,感興趣的可以了解一下
    2022-09-09
  • 詳解numpy.meshgrid()方法使用

    詳解numpy.meshgrid()方法使用

    這篇文章主要介紹了詳解numpy.meshgrid()方法使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • python爬蟲庫(kù)scrapy簡(jiǎn)單使用實(shí)例詳解

    python爬蟲庫(kù)scrapy簡(jiǎn)單使用實(shí)例詳解

    這篇文章主要介紹了python爬蟲庫(kù)scrapy簡(jiǎn)單使用實(shí)例詳解,需要的朋友可以參考下
    2020-02-02
  • 淺談Python中函數(shù)的參數(shù)傳遞

    淺談Python中函數(shù)的參數(shù)傳遞

    下面小編就為大家?guī)?lái)一篇淺談Python中函數(shù)的參數(shù)傳遞。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • 基于Python繪制一個(gè)會(huì)動(dòng)的3D立體粽子

    基于Python繪制一個(gè)會(huì)動(dòng)的3D立體粽子

    下周就要到端午節(jié)了,所以本文小編就來(lái)和大家分享一個(gè)有趣的Python項(xiàng)目——繪制會(huì)動(dòng)的3D立體粽子,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-06-06
  • Python使用sys.path查看當(dāng)前的模塊搜索路徑

    Python使用sys.path查看當(dāng)前的模塊搜索路徑

    sys.path 是 Python 中的一個(gè)列表,它用于存儲(chǔ)模塊搜索路徑,當(dāng)你使用 import 語(yǔ)句導(dǎo)入一個(gè)模塊時(shí),Python 會(huì)按照 sys.path 列表中的路徑順序來(lái)查找這個(gè)模塊,本文給大家介紹了Python使用sys.path查看當(dāng)前的模塊搜索路徑,需要的朋友可以參考下
    2025-02-02
  • python清除字符串里非字母字符的方法

    python清除字符串里非字母字符的方法

    這篇文章主要介紹了python清除字符串里非字母字符的方法,涉及Python字符串正則替換操作的相關(guān)技巧,需要的朋友可以參考下
    2015-07-07

最新評(píng)論