最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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è)最簡單的 http 服務(wù)器,能處理 http GET 請求。

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

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

問題一:實(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í)沒什么內(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 走一遍 bind、listen、accept、close 流程(子類 UDPServer 同理)。

重點(diǎn)關(guān)注 BaseServer,這里是網(wǎng)絡(luò)請求處理核心流程的實(shí)現(xiàn),文章最開頭的例子中 serve_forever() 這個(gè)入口方法就是在此類被實(shí)現(xià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)  # 注冊Server描述符并監(jiān)聽I/O讀事件
        while not self.__shutdown_request:  
            ready = selector.select(poll_interval)  # 超時(shí)時(shí)長poll_interval避免長時(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()  # 請求過來,I/O讀事件準(zhǔn)備好,開始處理請求
            self.service_actions()  
     finally:  
         self.__shutdown_request = False  
         self.__is_shut_down.set()

從 _handle_request_noblock() 中看到,一個(gè)網(wǎng)絡(luò)請求的處理流程無非就是 verify_request()、process_request()、shoutdown_request() 加上些許異常處理邏輯,比較簡明。在 finish_request() 中出現(xiàn) RequestHandlerClass 的類對象創(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ò)請求的處理流程
        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ò)請求流程大致抽象為 verify_request()、process_request()、shoutdown_request() 。如果考慮支持用戶自定義請求處理,還需要預(yù)留接口提供擴(kuò)展性。當(dāng)然如何要支持處理 HTTP 協(xié)議,還需要具備解析 HTTP 報(bào)文的能力,下文繼續(xù)探討。

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

前文介紹了內(nèi)置一個(gè)網(wǎng)絡(luò)請求的處理流程(等價(jià)于 HTTP Server 的運(yùn)行流程),一定程度上解釋了本節(jié)的問題,但欠缺一點(diǎn)細(xì)節(jié),沒有體現(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 中,但凡帶括號的都是方法,不帶括號的是類,從上往下也是父類到子類。本著代碼最大化復(fù)用的原則,父類 BaseRequestHandler 的 __init__() 中將工作流程確定下來,分別是 setup()、handler()、finish() 的先后調(diào)用順序。setup() 和 finish() 在子類 StreamRequestHandler 被實(shí)現(xiàn),最后在 BaseHTTPRequestHandler 類中實(shí)現(xiàn) HTTP 協(xié)議解析功能,以及用 HTTP method 來決定調(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í)長、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ú)貼過來,思路如下:

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

總結(jié)

python 內(nèi)置的 HTTP Server 實(shí)現(xià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構(gòu)建一個(gè)自己的聊天室

    使用python構(gòu)建一個(gè)自己的聊天室

    這篇文章主要介紹了使用python構(gòu)建一個(gè)自己的聊天室,有助于幫助小伙伴們理解掌握Python的socket編程,需要的朋友可以參考下
    2023-04-04
  • Python實(shí)現(xiàn)克里金插值法的過程詳解

    Python實(shí)現(xiàn)克里金插值法的過程詳解

    克里金算法提供的半變異函數(shù)模型有高斯、線形、球形、阻尼正弦和指數(shù)模型等,在對氣象要素場插值時(shí)球形模擬比較好。本文將用Python實(shí)現(xiàn)克里金插值法,感興趣的可以了解一下
    2022-11-11
  • 在Python中操作文件之truncate()方法的使用教程

    在Python中操作文件之truncate()方法的使用教程

    這篇文章主要介紹了在Python中操作文件之truncate()方法的使用教程,是Python入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • Python如何使用type()函數(shù)查看數(shù)據(jù)的類型

    Python如何使用type()函數(shù)查看數(shù)據(jù)的類型

    這篇文章主要介紹了Python如何使用type()函數(shù)查看數(shù)據(jù)的類型,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 基于Python+OpenCV制作屏幕錄制工具

    基于Python+OpenCV制作屏幕錄制工具

    這篇文章主要介紹了如何利用Python?OpenCV實(shí)現(xiàn)屏幕錄制工具的制作,文中的示例代碼講解詳細(xì),對我們的學(xué)習(xí)和工作有一定的價(jià)值,感興趣的可以學(xué)習(xí)一下
    2022-01-01
  • 基于Python的OpenCV骨架化圖像并顯示(skeletonize)

    基于Python的OpenCV骨架化圖像并顯示(skeletonize)

    這篇文章主要介紹了基于Python的OpenCV骨架化圖像并顯示(skeletonize),文中附含詳細(xì)的示例代碼,教大家來實(shí)現(xiàn)完成,有需要的可以參考下
    2021-08-08
  • python從zip中刪除指定后綴文件(推薦)

    python從zip中刪除指定后綴文件(推薦)

    這篇文章給大家介紹了python從zip中刪除指定后綴文件,然后再自動(dòng)壓縮,本文給大家提供了源碼參考,感興趣的朋友跟隨小編一起看看吧
    2019-12-12
  • python繪制柱狀圖的方法

    python繪制柱狀圖的方法

    這篇文章主要為大家詳細(xì)介紹了python繪制柱狀圖的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Python中的np.setdiff1d()函數(shù)詳解

    Python中的np.setdiff1d()函數(shù)詳解

    Python中的np.setdiff1d()函數(shù)可用于找出兩個(gè)序列集合中元素的差異,下面通過示例代碼給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • python操作音視頻ffmpeg-python對比pyav選擇

    python操作音視頻ffmpeg-python對比pyav選擇

    這篇文章主要介紹了python操作音視頻的選擇:ffmpeg-python對比pyav,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11

最新評論

新绛县| 乐亭县| 涡阳县| SHOW| 伊通| 察隅县| 北安市| 高青县| 谷城县| 句容市| 金山区| 疏附县| 阳春市| 江都市| 铁岭县| 巴青县| 嘉祥县| 旌德县| 奇台县| 邓州市| 循化| 上犹县| 原平市| 巴南区| 鄂尔多斯市| 河东区| 大新县| 涞水县| 竹山县| 农安县| 天门市| 葵青区| 锡林郭勒盟| 天全县| 体育| 沂源县| 大方县| 绥棱县| 黄石市| 玉树县| 泗阳县|