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

Python的socket模塊源碼中的一些實(shí)現(xiàn)要點(diǎn)分析

 更新時(shí)間:2016年06月06日 16:51:48   作者:人世間  
我們平時(shí)引入Python的socket模塊利用其中的方法可以輕松地寫出搭建socket通信的程序,今天我們就來(lái)看一下Python的socket模塊源碼中的一些實(shí)現(xiàn)要點(diǎn)分析,領(lǐng)略Python簡(jiǎn)潔代碼的一些背后功勞.

BaseServer 和 BaseRequestHandler
Python為網(wǎng)絡(luò)編程提高了更高級(jí)的封裝。SocketServer.py 提供了不少網(wǎng)絡(luò)服務(wù)的類。它們的設(shè)計(jì)很優(yōu)雅。Python把網(wǎng)絡(luò)服務(wù)抽象成兩個(gè)主要的類,一個(gè)是Server類,用于處理連接相關(guān)的網(wǎng)絡(luò)操作,另外一個(gè)則是RequestHandler類,用于處理數(shù)據(jù)相關(guān)的操作。并且提供兩個(gè)MixIn 類,用于擴(kuò)展 Server,實(shí)現(xiàn)多進(jìn)程或多線程。在構(gòu)建網(wǎng)絡(luò)服務(wù)的時(shí)候,Server 和 RequestHandler 并不是分開的,RequestHandler的實(shí)例對(duì)象在Server 內(nèi)配合 Server工作。

改模塊的主要幾個(gè)Server關(guān)系如下:

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

BaseServer 分析
BaseServer 通過(guò)__init__初始化,對(duì)外提供serve_forever和 handler_request方法。

init 初始化:

  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

__init__源碼很簡(jiǎn)單。主要作用是創(chuàng)建server對(duì)象,并初始化server地址和處理請(qǐng)求的class。熟悉socket編程應(yīng)該很清楚,server_address是一個(gè)包含主機(jī)和端口的元組。

serve_forever
創(chuàng)建了server對(duì)象之后,就需要使用server對(duì)象開啟一個(gè)無(wú)限循環(huán),下面來(lái)分析serve_forever的源碼。

  def serve_forever(self, poll_interval=0.5):
    self.__is_shut_down.clear()
    try:
      while not self.__shutdown_request:
        r, w, e = _eintr_retry(select.select, [self], [], [],
                    poll_interval)
        if self in r:
          self._handle_request_noblock()
    finally:
      self.__shutdown_request = False
      self.__is_shut_down.set()

serve_forever接受一個(gè)參數(shù)poll_interval,用于表示select輪詢的時(shí)間。然后進(jìn)入一個(gè)無(wú)限循環(huán),調(diào)用select方式進(jìn)行網(wǎng)絡(luò)IO的監(jiān)聽(tīng)。

如果select函數(shù)返回,表示有IO連接或數(shù)據(jù),那么將會(huì)調(diào)用_handle_request_noblock方法。

_handle_request_noblock
  def _handle_request_noblock(self):
    try:
      request, client_address = self.get_request()
    except socket.error:
      return
    if self.verify_request(request, client_address):
      try:
        self.process_request(request, client_address)
      except:
        self.handle_error(request, client_address)
        self.shutdown_request(request)

_handle_request_noblock方法即開始處理一個(gè)請(qǐng)求,并且是非阻塞。該方法通過(guò)get_request方法獲取連接,具體的實(shí)現(xiàn)在其子類。一旦得到了連接,調(diào)用verify_request方法驗(yàn)證請(qǐng)求。驗(yàn)證通過(guò),即調(diào)用process_request處理請(qǐng)求。如果中途出現(xiàn)錯(cuò)誤,則調(diào)用handle_error處理錯(cuò)誤,以及shutdown_request結(jié)束連接。

verify_request
  def verify_request(self, request, client_address):
    return True

該方法對(duì)request進(jìn)行驗(yàn)證,通常會(huì)被子類重寫。簡(jiǎn)單的返回True即可,然后進(jìn)入process_request方法處理請(qǐng)求。

process_request
  def process_request(self, request, client_address):
    self.finish_request(request, client_address)
    self.shutdown_request(request)

process_request方法是mixin的入口,MixIn子類通過(guò)重寫該方法,進(jìn)行多線程或多進(jìn)程的配置。調(diào)用finish_request完成請(qǐng)求的處理,同時(shí)調(diào)用shutdown_request結(jié)束請(qǐng)求。

finish_request
  def finish_request(self, request, client_address):
    self.RequestHandlerClass(request, client_address, self)

finish_request方法將會(huì)處理完畢請(qǐng)求。創(chuàng)建requestHandler對(duì)象,并通過(guò)requestHandler做具體的處理。

BaseRequestHandler 分析
所有requestHandler都繼承BaseRequestHandler基類。

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

該類會(huì)處理每一個(gè)請(qǐng)求。初始化對(duì)象的時(shí)候,設(shè)置請(qǐng)求request對(duì)象。然后調(diào)用setup方法,子類會(huì)重寫該方法,用于處理socket連接。接下來(lái)的將是handler和finish方法。所有對(duì)請(qǐng)求的處理,都可以重寫handler方法。

至此,整個(gè)Python提供的Server方式即介紹完畢。總結(jié)一下,構(gòu)建一個(gè)網(wǎng)絡(luò)服務(wù),需要一個(gè)BaseServer用于處理網(wǎng)絡(luò)IO,同時(shí)在內(nèi)部創(chuàng)建requestHandler對(duì)象,對(duì)所有具體的請(qǐng)求做處理。

BaseServer - BaseRequestHandler

__init__(server_address, RequestHandlerClass): 
  BaseServer.server_address
  BaseServer.RequestHandlerClass

serve_forever(): 

  select() 

  BaseServer._handle_request_noblock()

    BaseServer.get_request() -> request, client_addres

    BaseServer.verify_request()

      BaseServer.process_request()

        BaseServer.process_request()

          BaseServer.finish_request()

            BaseServer.RequestHandlerClass()

              BaseRequestHandler.__init__(request)

                BaseRequestHandler.request
                BaseRequestHandler.client_address = client_address

                BaseRequestHandler.setup()

                BaseRequestHandler.handle()

          BaseServer.shutdown_request()

            BaseServer.close_request()

      BaseServer.shutdown_request()

        BaseServer.close_request()

BaseServer 和 BaseRequestHandler是網(wǎng)絡(luò)處理的兩個(gè)基類。實(shí)際應(yīng)用中,網(wǎng)絡(luò)操作更多是使用 TCP 或 HTTP 協(xié)議。SocketServer.py 也提供了更高級(jí)的TCP、UDP封裝。下面就來(lái)看下關(guān)于TCP方面的網(wǎng)絡(luò)模塊(UDP和TCP的在代碼組織上差別不是特別大,暫且忽略)。

TCPServer
TCPServer 繼承了BaseServer,初始化的時(shí)候,進(jìn)行了socket套接字的創(chuàng)建。

def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
  BaseServer.__init__(self, server_address, RequestHandlerClass)
  self.socket = socket.socket(self.address_family,
                self.socket_type)
  if bind_and_activate:
    self.server_bind()
    self.server_activate()

__init__ 方法通過(guò) socket模塊創(chuàng)建了socket對(duì)象,然后進(jìn)行調(diào)用server_bind和server_activate。

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

server_bind 方法進(jìn)行socket對(duì)象的bind操作,以及設(shè)置socket相關(guān)屬性,如網(wǎng)絡(luò)地址的復(fù)用。

server_activate
def server_activate(self):
  self.socket.listen(self.request_queue_size)

server_activate 方法也比較簡(jiǎn)單,添加socket對(duì)象的listen。

get_request
該類最重要的方法就是 get_request。該方法進(jìn)行返回socket對(duì)象的請(qǐng)求連接。

  def get_request(self):
  """Get the request and client address from the socket.
  """
  return self.socket.accept()

get_request方法是在BaseServer基類中的_handle_request_noblock中調(diào)用,從那里里傳入套接字對(duì)象獲取的連接信息。如果是UDPServer,這里獲取的就是UDP連接。

此外,TCPServer還提供了一個(gè) fileno 方法,提供給基類的select調(diào)用返回文件描述符。

StreamRequestHandler
TCPServer實(shí)現(xiàn)了使用tcp套接字的網(wǎng)絡(luò)服務(wù),Handler方面則是對(duì)應(yīng)的StreamRequestHandler。它繼承了BaseRequestHandler?;惖膕etup方法和finish方法被它重寫,用于通過(guò)連接實(shí)現(xiàn)緩存文件的讀寫操作。

setup方法:

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)
  self.wfile = self.connection.makefile('wb', self.wbufsize)

setup判斷了是否使用nagle算法。然后設(shè)置對(duì)應(yīng)的連接屬性。最重要的就是創(chuàng)建了一個(gè)可讀(rfile)和一個(gè)可寫(wfile)的“文件”對(duì)象,他們實(shí)際上并不是創(chuàng)建了文件,而是封裝了讀取數(shù)據(jù)和發(fā)送數(shù)據(jù)的操作,抽象成為對(duì)文件的操作。可以理解為 self.rfile 就是讀取客戶端數(shù)據(jù)的對(duì)象,它有一些方法可以讀取數(shù)據(jù)。self.wfile則是用來(lái)發(fā)送數(shù)據(jù)給客戶端的對(duì)象。后面的操作,客戶端數(shù)據(jù)到來(lái)會(huì)被寫入緩沖區(qū)可讀,需要向客戶端發(fā)送數(shù)據(jù)的時(shí)候,只需要向可寫的文件中write數(shù)據(jù)即可。

實(shí)現(xiàn)TCP服務(wù)需要使用TCPServer和StreamRequestHandler共同協(xié)作。大致函數(shù)調(diào)用流程如下,函數(shù)調(diào)用用括號(hào)表示,賦值不帶括號(hào),沒(méi)有類前綴的表示系統(tǒng)調(diào)用:

TCPServer - StreamRequestHandler

__init__(server_address, RequestHandlerClass): 
  BaseServer.server_address
  BaseServer.RequestHandlerClass

  TCPServer.socket = socket.socket(self.address_family, self.socket_type)
  TCPServer.server_bind()
  TCPServer.server_activate()

serve_forever(): 

  select() 

  BaseServer._handle_request_noblock()

    TCPServer.get_request() -> request, client_addres
      socket.accept()

    BaseServer.verify_request()

      BaseServer.process_request()

        BaseServer.process_request()

          BaseServer.finish_request(request, client_address)

            BaseServer.RequestHandlerClass()

              BaseRequestHandler.__init__(request)

                BaseRequestHandler.request
                BaseRequestHandler.client_address = client_address

                StreamRequestHandler.setup()

                  StreamRequestHandler.connection = StreamRequestHandler.request
                  StreamRequestHandler.rfile
                  StreamRequestHandler.wfile

                BaseRequestHandler.handle()

                StreamRequestHandler.finsih()
                  StreamRequestHandler.wfile.close()
                  StreamRequestHandler.rfile.close()

          BaseServer.shutdown_request(request)
            TCPServer.shutdown()
              request.shutdown()
            TCPServer.close_request(request)
              request.close()

      TCPServer.shutdown_request(request)
        TCPServer.shutdown(request)
          request.shutdown()
        TCPServer.close_request(request)
          request.close()

最早關(guān)于介紹BaseServer的時(shí)候,我們知道python對(duì)BaseServer設(shè)計(jì)的時(shí)候,預(yù)留了可用于Mixin擴(kuò)展多線程或多進(jìn)程的接口。mixin通過(guò)復(fù)寫父類的parse_request方法實(shí)現(xiàn)。

ThreadingMixIn
ThreadingMixIn 類實(shí)現(xiàn)了多線程的方式,它只有兩個(gè)方法,分別是process_request和 process_request_thread方法。多進(jìn)程的方式是ForkingMixIn,暫且略過(guò)。

process_request
def process_request(self, request, client_address):
  t = threading.Thread(target = self.process_request_thread,
             args = (request, client_address))
  t.daemon = self.daemon_threads
  t.start()

process_request方法復(fù)寫了父類的此方法。以此為接口入口,對(duì)每一個(gè)請(qǐng)求,調(diào)用Thread開啟一個(gè)新的線程。每一個(gè)線程都綁定process_request_thread方法。

process_request_thread
  def process_request_thread(self, request, client_address):
    try:
      self.finish_request(request, client_address)
      self.shutdown_request(request)
    except:
      self.handle_error(request, client_address)
      self.shutdown_request(request)

process_request_thread方法和BaseServer里的parse_request幾乎一樣。只不過(guò)是多線程的方式調(diào)用。

使用的時(shí)候,通過(guò)多繼承調(diào)用接口,例如:

class ThreadingTCPServer(ThreadingMixIn, TCPServer): 
  pass

具體的調(diào)用過(guò)程如下:

ThreadingMixIn -- TCPServer - StreamRequestHandler

__init__(server_address, RequestHandlerClass): 
  BaseServer.server_address
  BaseServer.RequestHandlerClass

  TCPServer.socket = socket.socket(self.address_family, self.socket_type)
  TCPServer.server_bind()
  TCPServer.server_activate()

serve_forever(): 

  select() 

  BaseServer._handle_request_noblock()

    TCPServer.get_request() -> request, client_addres
      socket.accept()

    BaseServer.verify_request()

      BaseServer.process_request()

        ThreadingMixIn.process_request()
          t = threading.Thread(target = ThreadingMixIn.process_request_thread)

          ThreadingMixIn.process_request_thread

            BaseServer.finish_request(request, client_address)

              BaseServer.RequestHandlerClass()

                BaseRequestHandler.__init__(request)

                  BaseRequestHandler.request
                  BaseRequestHandler.client_address = client_address

                  StreamRequestHandler.setup()

                    StreamRequestHandler.connection = StreamRequestHandler.request
                    StreamRequestHandler.rfile
                    StreamRequestHandler.wfile

                  BaseRequestHandler.handle()

                  StreamRequestHandler.finsih()
                    StreamRequestHandler.wfile.close()
                    StreamRequestHandler.rfile.close()

            BaseServer.shutdown_request(request)
              TCPServer.shutdown()
                request.shutdown()
              TCPServer.close_request(request)
                request.close()

      TCPServer.shutdown_request(request)
        TCPServer.shutdown(request)
          request.shutdown()
        TCPServer.close_request(request)
          request.close()

相關(guān)文章

  • python爬蟲簡(jiǎn)單的添加代理進(jìn)行訪問(wèn)的實(shí)現(xiàn)代碼

    python爬蟲簡(jiǎn)單的添加代理進(jìn)行訪問(wèn)的實(shí)現(xiàn)代碼

    本文通過(guò)實(shí)例代碼給大家介紹了python爬蟲簡(jiǎn)單的添加代理進(jìn)行訪問(wèn),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-04-04
  • Python使用docx模塊實(shí)現(xiàn)刷題功能代碼

    Python使用docx模塊實(shí)現(xiàn)刷題功能代碼

    今天小編就為大家分享一篇Python使用docx模塊實(shí)現(xiàn)刷題功能代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • Python JWT 介紹和使用詳解

    Python JWT 介紹和使用詳解

    這篇文章主要介紹了Python JWT 介紹和使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Python如何在列表尾部添加元素

    Python如何在列表尾部添加元素

    這篇文章主要介紹了Python如何在列表尾部添加元素,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 聊一聊python常用的編程模塊

    聊一聊python常用的編程模塊

    好久沒(méi)用寫文章了,動(dòng)起筆來(lái)真不知道寫點(diǎn)啥來(lái),好吧,今天就給大家分享一些python常用的編程模塊吧,包括文件流的讀寫及如何刪除str中的特定字符,感興趣的朋友跟隨一起學(xué)習(xí)下吧
    2021-05-05
  • python使用QQ郵箱實(shí)現(xiàn)自動(dòng)發(fā)送郵件

    python使用QQ郵箱實(shí)現(xiàn)自動(dòng)發(fā)送郵件

    這篇文章主要為大家詳細(xì)介紹了python使用QQ郵箱實(shí)現(xiàn)自動(dòng)發(fā)送郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Python 序列的方法總結(jié)

    Python 序列的方法總結(jié)

    這篇文章主要介紹了Python 序列的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • Python-flask調(diào)用接口返回中文數(shù)據(jù)問(wèn)題

    Python-flask調(diào)用接口返回中文數(shù)據(jù)問(wèn)題

    這篇文章主要介紹了Python-flask調(diào)用接口返回中文數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • python使用Turtle庫(kù)繪制動(dòng)態(tài)鐘表

    python使用Turtle庫(kù)繪制動(dòng)態(tài)鐘表

    這篇文章主要為大家詳細(xì)介紹了python使用Turtle庫(kù)繪制動(dòng)態(tài)鐘表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Python dict字典基本操作(添加、修改、刪除鍵值對(duì))

    Python dict字典基本操作(添加、修改、刪除鍵值對(duì))

    本文主要介紹了Python dict字典基本操作,主要包括字典添加、修改、刪除鍵值對(duì)等,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09

最新評(píng)論