iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python的socket模块源码中的一些实现要点分析
  • 687
分享到

Python的socket模块源码中的一些实现要点分析

要点源码模块 2022-06-04 19:06:30 687人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

BaseServer 和 BaseRequestHandler python为网络编程提高了更高级的封装。SocketServer.py 提供了不少网络服务的类。它们的设计很优雅。Python把网络服务抽象

BaseServer 和 BaseRequestHandler
python网络编程提高了更高级的封装。SocketServer.py 提供了不少网络服务的类。它们的设计很优雅。Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。在构建网络服务的时候,Server 和 RequestHandler 并不是分开的,RequestHandler的实例对象在Server 内配合 Server工作。

改模块的主要几个Server关系如下:


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

BaseServer 分析
BaseServer 通过__init__初始化,对外提供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__源码很简单。主要作用是创建server对象,并初始化server地址和处理请求的class。熟悉socket编程应该很清楚,server_address是一个包含主机和端口的元组。

serve_forever
创建了server对象之后,就需要使用server对象开启一个无限循环,下面来分析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接受一个参数poll_interval,用于表示select轮询的时间。然后进入一个无限循环,调用select方式进行网络io的监听。

如果select函数返回,表示有IO连接或数据,那么将会调用_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方法即开始处理一个请求,并且是非阻塞。该方法通过get_request方法获取连接,具体的实现在其子类。一旦得到了连接,调用verify_request方法验证请求。验证通过,即调用process_request处理请求。如果中途出现错误,则调用handle_error处理错误,以及shutdown_request结束连接。


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

该方法对request进行验证,通常会被子类重写。简单的返回True即可,然后进入process_request方法处理请求。


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

process_request方法是mixin的入口,MixIn子类通过重写该方法,进行多线程或多进程的配置。调用finish_request完成请求的处理,同时调用shutdown_request结束请求。


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

finish_request方法将会处理完毕请求。创建requestHandler对象,并通过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()

该类会处理每一个请求。初始化对象的时候,设置请求request对象。然后调用setup方法,子类会重写该方法,用于处理socket连接。接下来的将是handler和finish方法。所有对请求的处理,都可以重写handler方法。

至此,整个Python提供的Server方式即介绍完毕。总结一下,构建一个网络服务,需要一个BaseServer用于处理网络IO,同时在内部创建requestHandler对象,对所有具体的请求做处理。

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是网络处理的两个基类。实际应用中,网络操作更多是使用 TCP 或 Http 协议。SocketServer.py 也提供了更高级的TCP、UDP封装。下面就来看下关于TCP方面的网络模块(UDP和TCP的在代码组织上差别不是特别大,暂且忽略)。

TCPServer
TCPServer 继承了BaseServer,初始化的时候,进行了socket套接字的创建。


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__ 方法通过 socket模块创建了socket对象,然后进行调用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 方法进行socket对象的bind操作,以及设置socket相关属性,如网络地址的复用。


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

server_activate 方法也比较简单,添加socket对象的listen。

get_request
该类最重要的方法就是 get_request。该方法进行返回socket对象的请求连接。


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

get_request方法是在BaseServer基类中的_handle_request_noblock中调用,从那里里传入套接字对象获取的连接信息。如果是UDPServer,这里获取的就是UDP连接。

此外,TCPServer还提供了一个 fileno 方法,提供给基类的select调用返回文件描述符。

StreamRequestHandler
TCPServer实现了使用tcp套接字的网络服务,Handler方面则是对应的StreamRequestHandler。它继承了BaseRequestHandler。基类的setup方法和finish方法被它重写,用于通过连接实现缓存文件的读写操作。

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算法。然后设置对应的连接属性。最重要的就是创建了一个可读(rfile)和一个可写(wfile)的“文件”对象,他们实际上并不是创建了文件,而是封装了读取数据和发送数据的操作,抽象成为对文件的操作。可以理解为 self.rfile 就是读取客户端数据的对象,它有一些方法可以读取数据。self.wfile则是用来发送数据给客户端的对象。后面的操作,客户端数据到来会被写入缓冲区可读,需要向客户端发送数据的时候,只需要向可写的文件中write数据即可。

实现TCP服务需要使用TCPServer和StreamRequestHandler共同协作。大致函数调用流程如下,函数调用用括号表示,赋值不带括号,没有类前缀的表示系统调用:

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

最早关于介绍BaseServer的时候,我们知道python对BaseServer设计的时候,预留了可用于Mixin扩展多线程或多进程的接口。mixin通过复写父类的parse_request方法实现。

ThreadingMixIn
ThreadingMixIn 类实现了多线程的方式,它只有两个方法,分别是process_request和 process_request_thread方法。多进程的方式是ForkingMixIn,暂且略过。


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方法复写了父类的此方法。以此为接口入口,对每一个请求,调用Thread开启一个新的线程。每一个线程都绑定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几乎一样。只不过是多线程的方式调用。

使用的时候,通过多继承调用接口,例如:


class ThreadingTCPServer(ThreadingMixIn, TCPServer): 
  pass

具体的调用过程如下:


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

--结束END--

本文标题: Python的socket模块源码中的一些实现要点分析

本文链接: https://www.lsjlt.com/news/14795.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JS中一些重要的api实现分析
    本篇内容主要讲解“JS中一些重要的api实现分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JS中一些重要的api实现分析”吧!核心要点:1.回调函数的参数有...
    99+
    2024-04-02
  • Python中的jieba源码分析
    本篇内容主要讲解“Python中的jieba源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python中的jieba源码分析”吧!前言jieba分词是Python 里面几个比较流行的中文...
    99+
    2023-06-02
  • Python中os模块和shutil模块的示例分析
    这篇文章将为大家详细讲解有关Python中os模块和shutil模块的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Pyth...
    99+
    2023-06-06
  • python中OS模块和time模块的示例分析
    这篇文章将为大家详细讲解有关python中OS模块和time模块的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、OS模块概述Python OS模块包含普遍的操作系统功能。如果你希望你的程序能够...
    99+
    2023-06-15
  • python中sys模块的示例分析
    小编给大家分享一下python中sys模块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!python版本: Python 2.7.61: sys是pyt...
    99+
    2023-06-14
  • Python中multiprocessing模块的Process类分析
    这篇文章主要讲解了“Python中multiprocessing模块的Process类分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python中multiprocessing模块的Pr...
    99+
    2023-06-17
  • python的numpy模块使用实例分析
    今天小编给大家分享一下python的numpy模块使用实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Numpy是Nu...
    99+
    2023-06-30
  • Python中包与模块的示例分析
    这篇文章主要为大家展示了“Python中包与模块的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Python中包与模块的示例分析”这篇文章吧。什么是 Python 的包与模块包的定义:简...
    99+
    2023-06-29
  • Python中urllib爬虫、request模块和parse模块的示例分析
    小编给大家分享一下Python中urllib爬虫、request模块和parse模块的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!urlliburlli...
    99+
    2023-06-14
  • Java实现俄罗斯方块的源码分享
    本文实现的功能有: 1、 初始化游戏窗口 2、初始化游戏的界面 3、初始化游戏的说明面板 4、随机生成下落方块 5、方块下落速度变化 6、判断方块是否可以下落 7、移除某一行方块上面...
    99+
    2024-04-02
  • Python PyQt5模块实现一个浏览器的示例代码
    目录1. 首先是环境的安装 (本人使用的是PyCharm,python3.6)2. 实现代码3. 运行结果4. Tips1. 首先是环境的安装 (本人使用的是PyCharm,pyth...
    99+
    2024-04-02
  • Bootstrap中模态窗口源码的示例分析
    这篇文章将为大家详细讲解有关Bootstrap中模态窗口源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下 +function ($)...
    99+
    2024-04-02
  • python中asyncore异步模块的实现
    目录模块常见方法asyncore 实例asyncore即是一个异步的socket封装,特别是dispatcher类中包含了很多异步调用的socket操作方法。 模块常见方法 这个模块...
    99+
    2023-01-18
    python asyncore异步模块 python asyncore异步
  • 怎么实现Java模块化系统的分析
    这篇文章给大家介绍怎么实现Java模块化系统的分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java 模块化系统自提出以来经历了很长的时间,直到 2014 年晚些时候才最终以 JSR(JSR-376) 定稿,而且这...
    99+
    2023-06-17
  • python模块中搜索路径的示例分析
    小编给大家分享一下python模块中搜索路径的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!python有哪些常用库python常用的库:1.reques...
    99+
    2023-06-14
  • 源码分析C++是如何实现string的
    目录深拷贝下string的实现COW方式下string的实现data()和c_str()的区别to_string是怎么实现的读完本文相信您可以回答以下问题: string的常见的实现...
    99+
    2023-05-14
    C++实现string源码 C++实现string C++ string
  • React实现合成事件的源码分析
    目录事件绑定事件触发结尾今天尝试学习 React 事件的源码实现。 React 版本为 18.2.0 React 中的事件,是对原生事件的封装,叫做合成事件。抽象出一层合成事件,是为...
    99+
    2022-12-08
    React实现合成事件 React合成事件
  • redisson实现分布式锁的源码解析
    目录redisson测试代码加锁设计锁续期设计锁的自旋重试解锁设计撤销锁续期解锁成功唤排队线程 redisson redisson 实现分布式锁的机制如下: 依赖版本 implem...
    99+
    2024-04-02
  • Python中变量,参数和模块的示例分析
    这篇文章主要介绍Python中变量,参数和模块的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1 变量首先,在python中,变量是存储在内存的值,程序在执行创建变量时会在内存中创建一个空间,并且根据变量的数...
    99+
    2023-06-22
  • AngularJS中使用模块组织代码的示例分析
    本篇文章给大家分享的是有关AngularJS中使用模块组织代码的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。下载 modu...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作