Python之路(第三十三篇) 網絡編程:socketserver深度解析

1、socketserver

模塊介紹

socketserver是標準庫中的一個高級模塊,用於網絡客戶端與服務器的實現。(version = "0.4")python

在python2中寫做SocketServer,在python3中寫做socketserver。linux

socoketserver兩個主要的類,一個是Server類,用於處理鏈接相關的網絡操做,另一個則是RequestHandler類,用於處理數據相關的操做,解決通訊問題。而且提供兩個MixIn 類,用於擴展 Server,實現多進程或多線程。算法

最基類的是服務器類BaseServer類,其中定義了相關的方法,不能直接使用這個類,只能用來繼承,在子類中有倆,是做爲同步服務器類使用,TCPServer和UDPServer,這兩個類主要是和socket編程的時候是相同的,也就是會阻塞鏈接。TCPServer有一個子類爲UNIXStreamServer,在UDPServer有一個子類爲UnixDatagramServer,這兩個是用在UNIX系統中的。數據庫

兩個混合類,一個是ForkingMixin,主要是用fork的(在linux系統中使用產生進程,在Windows系統中沒法使用fork產生進程),產生一個新的進程去處理;一個是ThreadingMixin,產生一個新的線程,主要是用來提供異步處理(簡單理解爲同時處理多個事情)的能力,其他tcpserver和udpserver組合,又產生了新的四個類,從而提供異步處理的能力。編程

socketserver框架

  在python中,socketserver是一個已集成的模塊,它有如下特色: 緩存

  • 可用SocketServer框架建立TCP和UDP服務器。服務器

  • 在後臺爲你作好每個基礎步驟。markdown

socketserver模塊的用法

  • 必須是BaseRequestHandler的子類網絡

  • 重寫handle()函數多線程

  • 調用serve_forever處理客戶端程序。

  • 對TCP 服務端來講

    • self.request是客戶端Socket對象

    • self.client_address是客戶端的詳細地址

 

socketserver模塊中的各個類

服務器類

5種類型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。 注意:BaseServer不直接對外服務。

 

請求類

3種類型:BaseRequestHandler,StreamRequestHandler,DatagramRequestHandler

 

minln類

ForkingMinIn ,ThreadingMinIn

 

混合類

ThreadingUDPServer, ThreadingTCPServer,ForkingUDPServer,ForkingTCPServer,

ThreadingUnixStreamServer,ThreadingUnixDatagramServer

socketserver模塊各個類繼承關係圖

 

 

服務器對象

 

  • class SocketServer.BaseServer:這是模塊中的全部服務器對象的超類。它定義了接口,以下所述,可是大多數的方法不實現,在子類中進行細化。

  • BaseServer.fileno():返回服務器監聽套接字的整數文件描述符。一般用來傳遞給select.select(), 以容許一個進程監視多個服務器。

  • BaseServer.handle_request():處理單個請求。處理順序:get_request(), verify_request(), process_request()。若是用戶提供handle()方法拋出異常,將調用服務器的handle_error()方法。若是self.timeout內沒有請求收到, 將調用handle_timeout()並返回handle_request()。

  • BaseServer.serve_forever(poll_interval=0.5): 處理請求,直到一個明確的shutdown()請求。每poll_interval秒輪詢一次shutdown。忽略self.timeout。若是你須要作週期性的任務,建議放置在其餘線程。

  • BaseServer.shutdown():告訴serve_forever()循環中止並等待其中止。python2.6版本。

  • BaseServer.address_family: 地址家族,好比socket.AF_INET和socket.AF_UNIX。

  • BaseServer.RequestHandlerClass:用戶提供的請求處理類,這個類爲每一個請求建立實例。

  • BaseServer.server_address:服務器偵聽的地址。格式根據協議家族地址的各不相同,請參閱socket模塊的文檔。

  • BaseServer.socketSocket:服務器上偵聽傳入的請求socket對象的服務器。

服務器類支持下面的類變量:

  • BaseServer.allow_reuse_address:服務器是否容許地址的重用。默認爲false ,而且可在子類中更改。

  • BaseServer.request_queue_size

請求隊列的大小。若是單個請求須要很長的時間來處理,服務器忙時請求被放置到隊列中,最多能夠放request_queue_size個。一旦隊列已滿,來自客戶端的請求將獲得 「Connection denied」錯誤。默認值一般爲5 ,但能夠被子類覆蓋。

  • BaseServer.socket_type:服務器使用的套接字類型; socket.SOCK_STREAM和socket.SOCK_DGRAM等。

  • BaseServer.timeout:超時時間,以秒爲單位,或 None表示沒有超時。若是handle_request()在timeout內沒有收到請求,將調用handle_timeout()。

下面方法能夠被子類重載,它們對服務器對象的外部用戶沒有影響。

  • BaseServer.finish_request():實際處理RequestHandlerClass發起的請求並調用其handle()方法。 經常使用。

  • BaseServer.get_request():接受socket請求,並返回二元組包含要用於與客戶端通訊的新socket對象,以及客戶端的地址。

  • BaseServer.handle_error(request, client_address):若是RequestHandlerClass的handle()方法拋出異常時調用。默認操做是打印traceback到標準輸出,並繼續處理其餘請求。

  • BaseServer.handle_timeout():超時處理。默認對於forking服務器是收集退出的子進程狀態,threading服務器則什麼都不作。

  • BaseServer.process_request(request, client_address) :調用finish_request()建立RequestHandlerClass的實例。若是須要,此功能能夠建立新的進程或線程來處理請求,ForkingMixIn和ThreadingMixIn類作到這點。經常使用。

  • BaseServer.server_activate():經過服務器的構造函數來激活服務器。默認的行爲只是監聽服務器套接字。可重載。

  • BaseServer.server_bind():經過服務器的構造函數中調用綁定socket到所需的地址。可重載。

  • BaseServer.verify_request(request, client_address):返回一個布爾值,若是該值爲True ,則該請求將被處理,反之請求將被拒絕。此功能能夠重寫來實現對服務器的訪問控制。默認的實現始終返回True。client_address能夠限定客戶端,好比只處理指定ip區間的請求。 經常使用。

請求處理器

處理器接收數據並決定如何操做。它負責在socket層之上實現協議(i.e., HTTP, XML-RPC, or AMQP),讀取數據,處理並寫反應。能夠重載的方法以下:

  • setup(): 準備請求處理. 默認什麼都不作,StreamRequestHandler中會建立文件相似的對象以讀寫socket.

  • handle(): 處理請求。解析傳入的請求,處理數據,併發送響應。默認什麼都不作。經常使用變量:self.request,self.client_address,self.server。

  • finish(): 環境清理。默認什麼都不作,若是setup產生異常,不會執行finish。

一般只須要重載handle。self.request的類型和數據報或流的服務不一樣。對於流服務,self.request是socket 對象;對於數據報服務,self.request是字符串和socket 。能夠在子類StreamRequestHandler或DatagramRequestHandler中重載,重寫setup()和finish() ,並提供self.rfile和self.wfile屬性。 self.rfile和self.wfile能夠讀取或寫入,以得到請求數據或將數據返回到客戶端。

 

socketserver模塊源碼翻譯


"""Generic socket server classes.

#  通用socket server 類


This module tries to capture the various aspects of defining a server:

# 該模塊盡力從各類不一樣的方面定義server:


For socket-based servers:

- address family:
        - AF_INET{,6}: IP (Internet Protocol) sockets (default)
        - AF_UNIX: Unix domain sockets
        - others, e.g. AF_DECNET are conceivable (see <socket.h>
- socket type:
        - SOCK_STREAM (reliable stream, e.g. TCP)
        - SOCK_DGRAM (datagrams, e.g. UDP)

#  對於socket-based servers:(對於socketserver類中的服務類的基類有下面內容)
#  -- address family:(地址家族)
#     - AF_INET{,6}: IP socket (default)
#     - AF_UNIX: Unix domain sockets
#     - others, 如 AF_DECNET (見<socket.h>) (不經常使用)
#  -- socket type:(套接字類型)
#     - SOCK_STREAM (可靠鏈接 TCP)
#     - SOCK_DGRAM (UDP)


For request-based servers (including socket-based):

- client address verification before further looking at the request
        (This is actually a hook for any processing that needs to look
         at the request before anything else, e.g. logging)
- how to handle multiple requests:
        - synchronous (one request is handled at a time)
        - forking (each request is handled by a new process)
        - threading (each request is handled by a new thread)

#  對於request-based servers:(對於通訊請求服務類的基類)
# -- client address在發出進一步的請求以前須要認證(這實際上把全部須要發出請求的進程在經過認證以前給阻塞住了)

# -- 如何處理多請求:(多進程多線程)
#    - 同步 (一次只能處理一個請求)
#    - forking (fork一個新的進程來處理一個請求)
#    - threading (建立一個新的線程來處理一個請求)


The classes in this module favor the server type that is simplest to
write: a synchronous TCP/IP server.  This is bad class design, but
save some typing.  (There's also the issue that a deep class hierarchy
slows down method lookups.)

# 在這個模塊的各類類中,最簡單的服務器類型就是synchronous TCP/IP server。
# 這是一個糟糕的類設計,可是也保存了一些設計的類型理念。


There are five classes in an inheritance diagram, four of which represent
synchronous servers of four types:

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

# 下面是五個類的繼承關係圖表,其中的四個表明四種類型的同步服務器:
        +----------------+
        | BaseServer |
        +----------------+
              |
              v
        +---------------+          +-------------------------+
        | TCPServer |------->| UnixStreamServer |
        +---------------+          +-------------------------+
              |
              v
        +---------------+          +-----------------------------+
        | UDPServer |------->| UnixDatagramServer |
        +---------------+          +-----------------------------+


Note that UnixDatagramServer derives from UDPServer, not from
UnixStreamServer -- the only difference between an IP and a Unix
stream server is the address family, which is simply repeated in both
unix server classes.

# 注意:UnixDatagramServer繼承於UDPServer,而不是UnixStreamServer,
# IP和Unix stream server之間僅有的差別就是address family,兩個服務器類的內容多數是簡單的重複。


Forking and threading versions of each type of server can be created
using the ForkingMixIn and ThreadingMixIn mix-in classes.  For
instance, a threading UDP server class is created as follows:

        class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

#   forking和threading 能夠被建立用於ForkingMixIn和TreadingMixIn mix-in類。
# 例如: threading UDP server類會被以下方式建立:
#  class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass


The Mix-in class must come first, since it overrides a method defined
in UDPServer! Setting the various member variables also changes
the behavior of the underlying server mechanism.

# Mix-in 這個類必須首先實現,由於它重寫了定義UDPServer的方法。
# 設置不一樣的成員變量也改變了基本的服務器構造方法。


To implement a service, you must derive a class from
BaseRequestHandler and redefine its handle() method.  You can then run
various versions of the service by combining one of the server classes
with your request handler class.

# 爲了實現一個服務,你必須從基類BaseRequestHandler中從新定義它的handle方法。
# 而後經過把服務類與你重寫的Handle方法類結合,以此運行新的服務類


The request handler class must be different for datagram or stream
services.  This can be hidden by using the request handler
subclasses StreamRequestHandler or DatagramRequestHandler.

# 請求處理類的TCP和UDP的方式是不一樣的,
# 這個能夠經過使用請求處理的子類StreamRequestHandler或者DatagramRequestHandler來隱藏。


Of course, you still have to use your head!

For instance, it makes no sense to use a forking server if the service
contains state in memory that can be modified by requests (since the
modifications in the child process would never reach the initial state
kept in the parent process and passed to each child).  In this case,
you can use a threading server, but you will probably have to use
locks to avoid two requests that come in nearly simultaneous to apply
conflicting changes to the server state.

# 固然,你還能夠思考其餘的方法。
#     例如,若是服務中包含請求修改的內存的狀態,那麼使用forking server沒有任何意義
# (由於在子進程中修改將不對父進程的初始化狀態有影響,父進程也不會把這個修改的參數傳遞給
# 其餘子進程)。這種狀況下,你可使用threading server,並且你更有可能須要用到「鎖」,
# 以此來避免兩個請求同時到達而使服務器狀態產生衝突。


On the other hand, if you are building e.g. an HTTP server, where all
data is stored externally (e.g. in the file system), a synchronous
class will essentially render the service "deaf" while one request is
being handled -- which may be for a very long time if a client is slow
to read all the data it has requested.  Here a threading or forking
server is appropriate.

 # 此外,若是你在搭建如HTTP服務器等,全部的數據都會存儲在外部(如文件系統中),
 # 當客戶端的一項請求被處理時,而且客戶端的讀取數據的速度很慢,synchronous class將
 # 會使服務不作出響應,這可能須要維持很長時間。


In some cases, it may be appropriate to process part of a request
synchronously, but to finish processing in a forked child depending on
the request data.  This can be implemented by using a synchronous
server and doing an explicit fork in the request handler class
handle() method.

# 在一些狀況下,請求同步可能須要恰當的方法,可是爲了在子進程中完成請求要受到請求數據
# 的影響。這能夠經過使用同步服務器來實現,而且在請求處理類中的Handle方法中明確指定fork
# 的進程。


Another approach to handling multiple simultaneous requests in an
environment that supports neither threads nor fork (or where these are
too expensive or inappropriate for the service) is to maintain an
explicit table of partially finished requests and to use a selector to
decide which request to work on next (or whether to handle a new
incoming request).  This is particularly important for stream services
where each client can potentially be connected for a long time (if
threads or subprocesses cannot be used).

# 另外一種處理多個同時發生的請求的方法是維繫一張明確的完成請求的表單,使用
# select()方法來斷定哪一個請求應該在接下來作出響應(或者判斷是否要處理新到來的請求),
# 當每個客戶端須要創建很長時間的鏈接時,這對於stream services來講很是重要。
# (前提是不使用線程和子進程的方法)


Future work:
- Standard classes for Sun RPC (which uses either UDP or TCP)
- Standard mix-in classes to implement various authentication
  and encryption schemes

#將來工做:
# -Sun RPC的標準類(使用UDP或TCP)
# -用於實現各類身份驗證的標準混合類和加密方案


XXX Open problems:
- What to do with out-of-band data?

#開放問題:如何處理帶外數據?


BaseServer:
- split generic "request" functionality out into BaseServer class.
  Copyright (C) 2000  Luke Kenneth Casson Leighton <lkcl@samba.org>

  example: read entries from a SQL database (requires overriding
  get_request() to return a table entry from the database).
  entry is processed by a RequestHandlerClass.

#BaseServer:
# -將泛型「請求」功能分離到BaseServer類中。
# 版權全部(C) 2000 Luke Kenneth Casson Leighton
# 示例:從SQL數據庫中讀取條目(須要重寫)
# get_request()以從數據庫返回表項。
# 條目由RequestHandlerClass處理。

"""

# Author of the BaseServer patch: Luke Kenneth Casson Leighton

__version__ = "0.4"

import socket
import selectors
import os
import errno
import sys

try:
    import threading
except ImportError:
    import dummy_threading as threading
from io import BufferedIOBase
from time import monotonic as time

__all__ = ["BaseServer", "TCPServer", "UDPServer",
           "ThreadingUDPServer", "ThreadingTCPServer",
           "BaseRequestHandler", "StreamRequestHandler",
           "DatagramRequestHandler", "ThreadingMixIn"]
if hasattr(os, "fork"):
    __all__.extend(["ForkingUDPServer", "ForkingTCPServer", "ForkingMixIn"])
if hasattr(socket, "AF_UNIX"):
    __all__.extend(["UnixStreamServer", "UnixDatagramServer",
                    "ThreadingUnixStreamServer",
                    "ThreadingUnixDatagramServer"])

# poll/select have the advantage of not requiring any extra file descriptor,
# contrarily to epoll/kqueue (also, they require a single syscall).
if hasattr(selectors, 'PollSelector'):
    _ServerSelector = selectors.PollSelector
else:
    _ServerSelector = selectors.SelectSelector


class BaseServer:
    """Base class for server classes.
    #服務類的基類
    Methods for the caller:
    # 可供調用的方法
    - __init__(server_address, RequestHandlerClass)
    - serve_forever(poll_interval=0.5)
    - shutdown()
    - handle_request()  # if you do not use serve_forever()
    - fileno() -> int   # for selector

    Methods that may be overridden:
    # 能夠被覆蓋的方法
    - server_bind()
    - server_activate()
    - get_request() -> request, client_address
    - handle_timeout()
    - verify_request(request, client_address)
    - server_close()
    - process_request(request, client_address)
    - shutdown_request(request)
    - close_request(request)
    - service_actions()
    - handle_error()

    Methods for derived classes:
    # 派生類的方法
    - finish_request(request, client_address)

    Class variables that may be overridden by derived classes or
    instances:
    # 能夠被派生類或重寫的類變量實例

    - timeout
    - address_family
    - socket_type
    - allow_reuse_address

    Instance variables:

    - RequestHandlerClass
    - socket

    """

    timeout = None

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        # 構造函數。能夠擴展,不要覆蓋。
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

    def server_activate(self):
        """Called by constructor to activate the server.

        May be overridden.
        # 由構造函數調用以激活服務器。可能會被覆蓋。
        """
        pass

    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.
        # 每次處理一個請求,直到關閉。爲每個poll_interval秒進行關閉輪詢。
        # 忽略了self.timeout。若是你須要作週期性的任務,那就作另外一個線程。
        """
        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.
            # 考慮使用另外一個文件描述符或鏈接到
            # 套接字來喚醒這個,而不是輪詢。輪詢會下降咱們的
            # 對關閉請求的響應性,會浪費cpu不少次。
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()

    def shutdown(self):
        """Stops the serve_forever loop.

        Blocks until the loop has finished. This must be called while
        serve_forever() is running in another thread, or it will
        deadlock.
        # 中止serve_forever循環。阻塞直到循環結束。這必須在while調用serve_forever()
        # 正在另外一個線程中運行,不然它會運行死鎖。
        """
        self.__shutdown_request = True
        self.__is_shut_down.wait()

    def service_actions(self):
        """Called by the serve_forever() loop.

        May be overridden by a subclass / Mixin to implement any code that
        needs to be run during the loop.
        # 由serve_forever()循環調用。可能被子類/ Mixin覆蓋以實現任何代碼須要在循環中運行。
        # 由serve_forever()循環調用。可能被子類/ Mixin覆蓋以實現任何代碼須要在循環中運行。
        """
        pass

    # The distinction between handling, getting, processing and finishing a
    # request is fairly arbitrary.  Remember:
    # 注意操做、獲取、併發的區別,完成一個請求是至關隨意的。
    # - handle_request() is the top-level call.  It calls selector.select(),
    # 操做請求函數是一個高級別的調用,它調用了selector.select().
    #   get_request(), verify_request() and process_request()
    # 獲取請求函數\驗證函數和進程線程函數
    # - get_request() is different for stream or datagram sockets
    # get_request()對於流(TCP)或數據報(UDP)套接字是不一樣的
    # - process_request() is the place that may fork a new process or create a
    #   new thread to finish the request
    # process_request()能夠派生一個新進程或建立一個新線程完成請求
    # - finish_request() instantiates the request handler class; this
    #   constructor will handle the request all by itself
    # finish_request()實例化請求處理程序類;這構造函數將本身處理請求

    def handle_request(self):
        """Handle one request, possibly blocking.
        # 處理一個請求,可能會阻塞。
        Respects self.timeout.
        #遵照超時規則
        """
        # Support people who used socket.settimeout() to escape
        # handle_request before self.timeout was available.
        # 支持使用socket.settimeout(),若是沒有將設置默認的
        # self.timeout以確保可用
        timeout = self.socket.gettimeout()
        if timeout is None:
            timeout = self.timeout
        elif self.timeout is not None:
            timeout = min(timeout, self.timeout)
        if timeout is not None:
            deadline = time() + timeout

        # Wait until a request arrives or the timeout expires - the loop is
        # necessary to accommodate early wakeups due to EINTR.
        # 等待直到請求到達或超時過時——循環是必須的,由於要適應中斷而被喚醒的各類狀況。
        with _ServerSelector() as selector:
            selector.register(self, selectors.EVENT_READ)

            while True:
                ready = selector.select(timeout)
                if ready:
                    return self._handle_request_noblock()
                else:
                    if timeout is not None:
                        timeout = deadline - time()
                        if timeout < 0:
                            return self.handle_timeout()

    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().
        #我假設select .select()返回的是套接字在調用此函數以前可讀,
        所以應該在get_request()中沒有阻塞風險。
        """
        try:
            request, client_address = self.get_request()
        except OSError:
            return
        if self.verify_request(request, client_address):
            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 handle_timeout(self):
        """Called if no new request arrives within self.timeout.
        # 若是self.timeout中沒有新請求到達,則調用。
        Overridden by ForkingMixIn.
        被ForkingMixIn類覆蓋
        """
        pass

    def verify_request(self, request, client_address):
        """Verify the request.  May be overridden.
        # 驗證這個請求,可能被覆蓋
        Return True if we should proceed with this request.
        # 若是咱們繼續這個請求,返回True。
        """
        return True

    def process_request(self, request, client_address):
        """Call finish_request.
        # 調用 finish_request
        Overridden by ForkingMixIn and ThreadingMixIn.
        # 被ForkingMixIn and ThreadingMixIn覆蓋
        """
        self.finish_request(request, client_address)
        self.shutdown_request(request)

    def server_close(self):
        """Called to clean-up the server.
        # 調用以清理這個服務
        May be overridden.
        # 可能被覆蓋
        """
        pass

    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        # 經過實例化RequestHandlerClass來完成一個請求。
        self.RequestHandlerClass(request, client_address, self)

    def shutdown_request(self, request):
        """Called to shutdown and close an individual request."""
        # 調用以關閉和關閉單個請求
        self.close_request(request)

    def close_request(self, request):
        """Called to clean up an individual request."""
        # 調用以清理單個請求
        pass

    def handle_error(self, request, client_address):
        """Handle an error gracefully.  May be overridden.
        # 優雅地處理錯誤。可能會被覆蓋。
        The default is to print a traceback and continue.
        # 默認狀況是打印回溯記錄並繼續程序。
        """
        print('-' * 40, file=sys.stderr)
        print('Exception happened during processing of request from',
              client_address, file=sys.stderr)
        import traceback
        traceback.print_exc()
        print('-' * 40, file=sys.stderr)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.server_close()


class TCPServer(BaseServer):
    """Base class for various socket-based server classes.
    # 用於各類基於套接字的服務器類的基類
    Defaults to synchronous IP stream (i.e., TCP).
    #默認爲同步IP流(即TCP)
    Methods for the caller:
    # 可調用的方法
    - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
    - serve_forever(poll_interval=0.5)
    - shutdown()
    - handle_request()  # if you don't use serve_forever()
    - fileno() -> int   # for selector

    Methods that may be overridden:
    # 可能被覆蓋的方法
    - server_bind()
    - server_activate()
    - get_request() -> request, client_address
    - handle_timeout()
    - verify_request(request, client_address)
    - process_request(request, client_address)
    - shutdown_request(request)
    - close_request(request)
    - handle_error()

    Methods for derived classes:
    # 派生類的方法
    - finish_request(request, client_address)

    Class variables that may be overridden by derived classes or
    instances:
    # 能夠被派生類或重寫的類變量實例
    - timeout
    - address_family
    - socket_type
    - request_queue_size (only for stream sockets)
    - allow_reuse_address

    Instance variables:
    # 實例化變量
    - server_address
    - RequestHandlerClass
    - socket

    """

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = False

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        # 構造函數。能夠擴展,不要覆蓋。
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise

    def server_bind(self):
        """Called by constructor to bind the socket.
        # 由構造函數調用以綁定套接字。
        May be overridden.
        # 可能被覆蓋
        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        """Called by constructor to activate the server.
        # 由構造函數調用以激活服務器
        May be overridden.
         # 可能被覆蓋
        """
        self.socket.listen(self.request_queue_size)

    def server_close(self):
        """Called to clean-up the server.
        # 調用以清理服務
        May be overridden.
        # 可能被覆蓋
        """
        self.socket.close()

    def fileno(self):
        """Return socket file number.
        # 返回套接字的文件描述符
        Interface required by selector.
        # selector須要的接口
        """
        return self.socket.fileno()

    def get_request(self):
        """Get the request and client address from the socket.
        # 從套接字獲取請求和客戶端地址
        May be overridden.
        # 可能被覆蓋
        """
        return self.socket.accept()

    def shutdown_request(self, request):
        """Called to shutdown and close an individual request."""
        # 調用以關閉和關閉單個請求。
        try:
            # explicitly shutdown.  socket.close() merely releases
            # 明確地關閉。socket.close()僅僅是釋放
            # the socket and waits for GC to perform the actual close.
            # 套接字並等待GC執行實際的關閉。
            request.shutdown(socket.SHUT_WR)
        except OSError:
            pass  # some platforms may raise ENOTCONN here #一些平臺可能會在這裏引起ENOTCONN錯誤
        self.close_request(request)

    def close_request(self, request):
        """Called to clean up an individual request."""
        # 調用以清理單個的請求
        request.close()


class UDPServer(TCPServer):
    """UDP server class."""
    # UDP服務類
    allow_reuse_address = False

    socket_type = socket.SOCK_DGRAM

    max_packet_size = 8192

    def get_request(self):
        data, client_addr = self.socket.recvfrom(self.max_packet_size)
        return (data, self.socket), client_addr

    def server_activate(self):
        # No need to call listen() for UDP.
        # UDP服務類不須要listen()
        pass

    def shutdown_request(self, request):
        # No need to shutdown anything.
        #  UDP服務類不須要
        self.close_request(request)

    def close_request(self, request):
        # No need to close anything.
        # UDP服務類不須要
        pass


if hasattr(os, "fork"):
    class ForkingMixIn:
        """Mix-in class to handle each request in a new process."""
        # 混合類在新進程中處理每一個請求
        timeout = 300
        active_children = None
        max_children = 40

        def collect_children(self):
            """Internal routine to wait for children that have exited."""
            # 等待子進程離開的內部程序
            if self.active_children is None:
                return

            # If we're above the max number of children, wait and reap them until
            # we go back below threshold. Note that we use waitpid(-1) below to be
            # able to collect children in size(<defunct children>) syscalls instead
            # of size(<children>): the downside is that this might reap children
            # which we didn't spawn, which is why we only resort to this when we're
            # above max_children.

            # 若是咱們超過了子進程的最大數量,那須要等待並回收他們直到咱們回到閾值如下。
            # 注意,咱們使用下面的waitpid(-1)來回收(<殭屍進程>)系統調用代替了回收子進程
            # 缺點是這可能會沒有產生子進程,這就是爲何咱們只有在最大子進程數量之上
            # 纔會使用它。
            while len(self.active_children) >= self.max_children:
                try:
                    pid, _ = os.waitpid(-1, 0)
                    self.active_children.discard(pid)
                except ChildProcessError:
                    # we don't have any children, we're done
                    # 沒有任何子進程
                    self.active_children.clear()
                except OSError:
                    break

            # Now reap all defunct children.
            # 如今回收全部的殭屍進程
            for pid in self.active_children.copy():
                try:
                    pid, _ = os.waitpid(pid, os.WNOHANG)
                    # if the child hasn't exited yet, pid will be 0 and ignored by
                    # 若是這個子進程沒退出,PID會是0並被忽略
                    # discard() below
                    # 丟棄pid
                    self.active_children.discard(pid)
                except ChildProcessError:
                    # someone else reaped it
                    # 其餘的被回收了
                    self.active_children.discard(pid)
                except OSError:
                    pass

        def handle_timeout(self):
            """Wait for zombies after self.timeout seconds of inactivity.
            等着不活動超時秒殭屍進程出現
            May be extended, do not override.
            可能被擴展,不要被覆蓋
            """
            self.collect_children()

        def service_actions(self):
            """Collect the zombie child processes regularly in the ForkingMixIn.
            按期在ForkingMixin中收集殭屍子進程
            service_actions is called in the BaseServer's serve_forver loop.
            service_actions在BaseServer的serve_forver循環中被調用。
            """
            self.collect_children()

        def process_request(self, request, client_address):
            """Fork a new subprocess to process the request."""
            # 派生一個新的子進程來處理請求
            pid = os.fork()
            if pid:
                # Parent process
                # 父進程
                if self.active_children is None:
                    self.active_children = set()
                self.active_children.add(pid)
                self.close_request(request)
                return
            else:
                # Child process.
                # 子進程
                # This must never return, hence os._exit()!
                # 這裏必須永遠不能return,所以os._exit()!
                status = 1
                try:
                    self.finish_request(request, client_address)
                    status = 0
                except Exception:
                    self.handle_error(request, client_address)
                finally:
                    try:
                        self.shutdown_request(request)
                    finally:
                        os._exit(status)


class ThreadingMixIn:
    """Mix-in class to handle each request in a new thread."""
    # 混合類以處理新線程中的每一個請求。
    # Decides how threads will act upon termination of the
    # main process
    # 決定線程在主進程終止後將如何操做
    daemon_threads = False

    def process_request_thread(self, request, client_address):
        """Same as in BaseServer but as a thread.
        # 和在BaseServer中同樣,但做爲一個線程。
        In addition, exception handling is done here.
        # 此外,這裏還進行了異常處理。
        """
        try:
            self.finish_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
        finally:
            self.shutdown_request(request)

    def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        # 啓動一個新的線程來處理請求。
        t = threading.Thread(target=self.process_request_thread,
                             args=(request, client_address))
        t.daemon = self.daemon_threads
        t.start()


if hasattr(os, "fork"):
    class ForkingUDPServer(ForkingMixIn, UDPServer): pass


    class ForkingTCPServer(ForkingMixIn, TCPServer): pass


class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass


class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass


if hasattr(socket, 'AF_UNIX'):
    class UnixStreamServer(TCPServer):
        address_family = socket.AF_UNIX


    class UnixDatagramServer(UDPServer):
        address_family = socket.AF_UNIX


    class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass


    class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass


class BaseRequestHandler:
    """Base class for request handler classes.
    # 請求處理程序類的基類。
    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    這個類是爲每一個要處理的請求實例化的。構造函數設置實例化變量request client_address,
    而後調用handle()方法,實現一個具體的服務,
    您須要作的就是派生一個類定義handle()方法。

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.

    # handle()方法能夠將request查找爲self.request,客戶端地址爲self.client_address,
    # 以及服務器(以防萬一)須要訪問每一個服務器的信息)做爲self.server。
    # 每一個請求都建立單獨的實例對象,handle()方法能夠定義其餘任意實例變量。

    """

    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


# The following two classes make it possible to use the same service
# class for stream or datagram servers.

# 如下兩個類使使用相同的服務成爲可能
# 用於流服務器(TCP)或數據報服務器(UDP)。

# Each class sets up these instance variables:
# - rfile: a file object from which receives the request is read
# - wfile: a file object to which the reply is written
# When the handle() method returns, wfile is flushed properly

# 每一個類都設置這些實例變量:
# - rfile:接收請求的文件對象
# - wfile:寫回復的文件對象
# 當handle()方法返回時,wfile被適時地刷新


class StreamRequestHandler(BaseRequestHandler):
    """Define self.rfile and self.wfile for stream sockets."""
    # 爲流服務器定義self.rfile和self.wfile

    # Default buffer sizes for rfile, wfile.
    # We default rfile to buffered because otherwise it could be
    # really slow for large data (a getc() call per byte); we make
    # wfile unbuffered because (a) often after a write() we want to
    # read and we need to flush the line; (b) big writes to unbuffered
    # files are typically optimized by stdio even when big reads
    # aren't.

    # 默認的rfile, wfile 緩衝區大小
    #咱們默認將rfile設置爲緩存,不然可能會這樣對於大數據
    # (每一個字節一個 getc()調用)來講確實很慢;
    #咱們作wfile未緩存,由於(a)一般在寫入()以後閱讀,咱們須要不斷刷新;
    #標準輸入輸出一般會對未緩衝的大文件寫入操做進行優化,即便是大文件的讀操做也是如此。

    rbufsize = -1
    wbufsize = 0

    # A timeout to apply to the request socket, if not None.
    # 應用於請求套接字(若是不是None)的超時。
    timeout = None

    # Disable nagle algorithm for this socket, if True.
    # Use only when wbufsize != 0, to avoid small packets.

    # 若是爲這個套接字,禁用nagle算法。
    #僅在wbufsize != 0時使用,以免小數據包。
    disable_nagle_algorithm = False

    def setup(self):
        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()


class _SocketWriter(BufferedIOBase):
    """Simple writable BufferedIOBase implementation for a socket

    Does not hold data in a buffer, avoiding any need to call flush()."""
    # 一個套接字的簡單可寫的BufferedIOBase實現
    # 不將數據保存在緩衝區中,避免調用flush()

    def __init__(self, sock):
        self._sock = sock

    def writable(self):
        return True

    def write(self, b):
        self._sock.sendall(b)
        with memoryview(b) as view:
            return view.nbytes

    def fileno(self):
        return self._sock.fileno()


class DatagramRequestHandler(BaseRequestHandler):
    """Define self.rfile and self.wfile for datagram sockets."""

    # 定義self.rfile和self.wfile用於數據報套接字
    def setup(self):
        from io import BytesIO
        self.packet, self.socket = self.request
        self.rfile = BytesIO(self.packet)
        self.wfile = BytesIO()

    def finish(self):
        self.socket.sendto(self.wfile.getvalue(), self.client_address)

  



先複習繼承知識

例子1

  
  class Base:
  ​
      def __init__(self,name):
          self.name=name
  ​
      def func(self):
          print("from base")
  ​
  ​
  class Son(Base):
      def func(self):
          print("from son")
  ​
  sonobj= Son("nick")
  sonobj.func()

  

輸出結果

  
  from son

  

分析下執行過程

一、加載class Base ,加載class Son

二、執行sonobj= Son("nick")語句,生成sonobj對象,這裏首先尋找__init__初始化方法,Son類沒有,找Son的父類Base,找到父類的__init__方法,執行父類的方法,將參數"nick"傳入,注意這裏的self實際上是sonobj,即類Son的對象,而非Base的對象,執行self.name=name,爲sonobj對象增長了name屬性,初始化過程結束。

三、執行sonobj.func()語句,執行對象sonobj的func方法,首先要在這個sonobj對象自身的方法下面找,發現有func方法就執行該方法,而不須要再到父類Base執行func方法。

 

例子2

  
  class Base:
  ​
  ​
  ​
      def func(self):
          print("from base")
  ​
  ​
  class Son(Base):
      def __init__(self,name):
          self.name=name
  ​
      def func(self):
          print("from son")
  ​
  class Base2:
  ​
      def func(self):
          print("from base2")
  ​
  class Grandson(Base2,Son):
      pass
  ​
  sonobj= Grandson("nick")
  sonobj.func()

  

輸出結果

 
 from base2

  

簡單分析執行結果

一、在python3中全部的類都是新式類,執行的繼承順序是廣度優先,因此類Grandson繼承的順序是

 

二、首先執行初始化方法,在base2中沒找到,而後在Son中找到了,執行該方法,增長了對象的name屬性,

三、執行func方法,根據繼承順序,先左後右,先在base2中找到了,就直接執行func方法,再也不繼續尋找。程序結束。

 

socketserver源碼分析tcp版本

服務端

  import socketserver
  ​
  ​
  class Myserver(socketserver.BaseRequestHandler):  #
  ​
      def handle(self):
          print("服務器開始運行了")
          print(self.request)  # 至關於socket裏的conn連接
          print(self.client_address)  # 至關於socket裏的addr,即客戶端的ip地址
          while True:
              try:
                  res = self.request.recv(1024)
                  print(res.decode("utf-8"))
                  msg = input(">>>").strip()
                  self.request.send(msg.encode("utf-8"))
              except Exception as e:
                  print(e)
                  break
  ​
  ​
  if __name__ == "__main__":
      ip, port = "127.0.0.1", 8090
      serverobj = socketserver.ThreadingTCPServer((ip, port), Myserver)
      serverobj.serve_forever()

  

客戶端

  
  import socket
  ​
  ip, port = "127.0.0.1", 8090
  buffer_size = 1024
  ​
  client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  client.connect((ip, port))
  ​
  while True:
      msg = input(">>>").strip()
      if msg == "q": break
      if not msg: continue
      client.send(('客戶端A:' + msg).encode("utf-8"))
      res = client.recv(buffer_size)
      print(res.decode("utf-8"))

  

分析內部執行過程:

一、

加載socketserver模塊,加載class Myserver,執行ip, port = "127.0.0.1", 8090語句

二、

執行

serverobj = socketserver.ThreadingTCPServer((ip, port), Myserver)

  

語句,尋找初始化方法,根據socketserver模塊各個類繼承關係圖可知,ThreadingTCPServer繼承了ThreadingMixIn, TCPServer,而TCPServer又繼承了BaseServer。

2.一、

首先在左邊的ThreadingMixIn尋找初始化方法,沒有找到,再在TCPServer裏尋找,找到了__init__方法,執行該方法。

TCPServer下的__init__方法

這裏的參數server_address就是傳入的(ip, port),而RequestHandlerClass就是本身定義的類Myserver

 

2.1.一、

這裏首先執行BaseServer下的__init__方法

BaseServer下的__init__方法

 

執行該方法下面的4個語句,注意這裏的self是ThreadingTCPServer的對象,而非BaseServer的對象,由於此次調用是ThreadingTCPServer的對象發起的,即服務端中的serverobj對象。

爲serverobj增長了4個屬性,將(ip,port)賦值爲self.server_addressz屬性,本身定義的類 Myserver賦值爲RequestHandlerClass屬性,執行threading.Event()賦值爲self.__is_shut_down。

 

2.1.二、

執行

  self.socket = socket.socket(self.address_family,self.socket_type)

  

語句

建立socket對象並將socket對象賦值給self.socket屬性

2.1.三、

執行

  if bind_and_activate:
      try:
          self.server_bind()
          self.server_activate()
      except:
          self.server_close()
          raise
 

  

 

2.1.3.一、

執行self.server_bind()語句,注意這裏的self仍是serverobj對象,因此須要從新開始尋找server_bind方法,從左邊的ThreadingMixIn尋找,沒有找到,在右邊的TCPServer裏尋找,找到了server_bind()方法

 

因爲self.allow_reuse_address=False,不執行這裏的if語句

self.socket.bind(self.server_address)

  

執行了socket對象的綁定ip\port,

執行

 self.server_address = self.socket.getsockname()

  

將返回套接字本身的地址。一般是一個元組(ipaddr,port)從新賦值給self.server_address 屬性

 

2.1.3.二、

執行self.server_activate()語句,與上面同樣,先從ThreadingMixIn找,再找TCPServer,執行TCPServer下面的server_activate方法,

 
 self.socket.listen(self.request_queue_size)

  

執行socket對象的監聽動做

2.1.3.三、

若是出錯,執行self.server_close(),仍是同樣的尋找過程,這裏就不寫了,直接寫找到的結果,執行

TCPServer下面的server_close()方法,執行self.socket.close()語句,即關閉socket

 

2.二、

至此,serverobj對象的初始化方法__init__才執行完

三、

執行serverobj.serve_forever()語句

3.一、

與上面同樣,也是從從ThreadingMixIn找serve_foreve()方法,再找TCPServer下面有沒有serve_foreve()方法,發現TCPServer下面也沒有,最後找TCPServer的父類BaseServer,在BaseServer下面找到了serve_forever()方法

 

代碼以下

  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)
  ​
              while not self.__shutdown_request:
                  ready = selector.select(poll_interval)
                  if ready:
                      self._handle_request_noblock()
  ​
                  self.service_actions()
      finally:
          self.__shutdown_request = False
          self.__is_shut_down.set()

  

3.1.一、

執行self.__is_shut_down.clear()語句,清理線程事件狀態(後面網絡編程部分會學到)

3.1.二、

執行try語句

  
   with _ServerSelector() as selector:
              selector.register(self, selectors.EVENT_READ) #註冊,表示關心EVENT_READ事件

  

執行IO多路複用(後面網絡編程部分會學到),

3.1.三、

執行

          while not self.__shutdown_request:
              ready = selector.select(poll_interval)# 查詢事件,在poll_interval時間裏查詢是否是發生了新的事件
              if ready:   #若是有新事件,則經過下面的語句處理請求
                  self._handle_request_noblock()

  

 

因爲self.__shutdown_request默認值爲False,因此這裏是個死循環

3.1.3.一、

執行

self._handle_request_noblock()語句

尋找handle_request_noblock()方法與上面相似,最後是在BaseServer下面找到了handle_request_noblock()方法

  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):
          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)

  

3.1.3.1.一、

執行try語句

  try:
      request, client_address = self.get_request()

  

執行self.get_request()方法,將結果分別賦值給request, client_address

首先和上面同樣,尋找到對象的get_request()方法,在TCPServer下面找到了get_request()方法

  return self.socket.accept()

  

即socket對象的accept()操做,得到與用戶鏈接的socket和用戶地址。即這裏的request是conn連接

3.1.3.1.二、

執行

   if self.verify_request(request, client_address):
          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

  

verify_request()來驗證用戶請求,找到BaseServer下面的verify_request(),

該方法默認返回True。

執行self.process_request(request, client_address)語句

找到ThreadingMixIn類下面的process_request()方法

 

  
  def process_request(self, request, client_address):
      """Start a new thread to process the request."""
      t = threading.Thread(target = self.process_request_thread,
                           args = (request, client_address)) #建立多線程
      t.daemon = self.daemon_threads 
      t.start() #啓動線程

  

這裏建立了多線程,表示能夠同時執行多個任務了。

這裏多線程調用process_request_thread()方法

即執行

  def process_request_thread(self, request, client_address):
      """Same as in BaseServer but as a thread.
  ​
      In addition, exception handling is done here.
  ​
      """
      try:
          self.finish_request(request, client_address)
      except Exception:
          self.handle_error(request, client_address)
      finally:
          self.shutdown_request(request)

  

執行self.finish_request(request, client_address),即從新尋找finish_request()方法,在找到BaseServer下面的finish_request()方法

  def finish_request(self, request, client_address):
      """Finish one request by instantiating RequestHandlerClass."""
      self.RequestHandlerClass(request, client_address, self)

  

這裏是將本身建立的類Myserver實例化,即執行Myserver(request, client_address, self)Z實例化過程,這裏產生的實例與serverobj沒有任何關係,這裏要找到初始化方法,Myserver下沒有,找Myserver繼承的socketserver.BaseRequestHandler

在socketserver.BaseRequestHandler下找到了init方法

 

  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 __init__(self, request, client_address, server):

中self仍是serverobj,增長了self.request、self.client_address、self.server 等屬性給Myserver建立的對象

這裏的self.server就是Myserver這個類,這裏定義了一些空的方法self.setup()、self.handle()、self.finish(),子類能夠經過派生增長功能

 

若是 try: self.process_request(request, client_address)出錯則執行

      except Exception:
          self.handle_error(request, client_address)
          self.shutdown_request(request)

  

調用執行BaseServer下面的handle_error()方法處理下錯誤,

而後調用執行BaseServer下面的shutdown_request()方法,

  self.close_request(request)

  

而BaseServer這裏又調用執行close_request()方法,這裏又要從新根據繼承關係找close_request()方法,在TCPServer下面找到close_request()

執行request.close()語句,即conn.close().

 

3.1.3.1.三、

其餘狀況執行


self.shutdown_request(request)

  

也是執行上面的request.close()語句。

3.1.四、

至此,self._handle_request_noblock()執行完成。

 

3.1.五、

執行self.service_actions()語句

3.1.6

執行

  finally:
      self.__shutdown_request = False
      self.__is_shut_down.set()

  

設置__shutdown_request屬性,更改self.__is_shut_down的屬性

3.2

serverobj.serve_forever()執行完成

 

 

 

 

 

參考資料

[1]http://www.javashuo.com/article/p-xbmqbqok-gr.html

相關文章
相關標籤/搜索