zoukankan      html  css  js  c++  java
  • Python面试题之解读Socketserver & Tcpserver

    在解析socketserver是如工作之前,我们先看看socektserver类的继承关系图:

      请求类继承关系:

              

      server类继承关系:

              

      有了上面的继承关系图后,我们解析socketserver就轻松多了,下面,我们从代码开始,慢慢揭开socketserver面纱:

    复制代码
    import socketserver
    import struct, json, os
    
    class FtpServer(socketserver.BaseRequestHandler):
        coding = 'utf-8'
        server_dir = 'file_upload'
        max_packet_size = 1024
        BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    
        def handle(self):
            print(self.request)
            while True:
                data = self.request.recv(4)
                data_len = struct.unpack('i', data)[0]
                head_json = self.request.recv(data_len).decode(self.coding)
                head_dic = json.loads(head_json)
                cmd = head_dic['cmd']
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func(head_dic)
    
        def put(self):
            pass
    
        def get(self):
            pass
    
    if __name__ == '__main__':
        HOST, PORT = "localhost", 9999
        with socketserver.ThreadingTCPServer((HOST, PORT), FtpServer) as server:
            server.serve_forever()
    复制代码

      我们通过socketserver.ThreadingTCPServer实例化对象server,那么此时应用调用类的__init__方法,前往ThreadingTCPServer类看看:

    class ThreadingTCPServer(ThreadingMixIn, UDPServer): pass

      发现這个类啥都没写,我们知道,如果一个类什么方法都没有定义,那么它的方法肯定都是从其父类继承而来,接着,先到ThreadingMinIn里面看看,

    class ThreadingMixIn:
        daemon_threads = False
    
        def process_request_thread(self, request, client_address):
            passdef process_request(self, request, client_address):
           pass

      这个类也没有__init__方法,因此,我们应该去右继承的父类TCPserver中找:

    复制代码
    class TCPServer(BaseServer):
        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):
            BaseServer.__init__(self, server_address, RequestHandlerClass)#
            self.socket = socket.socket(self.address_family,self.socket_type)  # 创建套接字对象
            if bind_and_activate:
                try:
                    self.server_bind()  #绑定端口和IP
                    self.server_activate()  # 监听端口
                except:
                    self.server_close()
                    raise
    复制代码

      看到Tcpserver的__init__方法,完成了以下几件事:

        创建套接字,绑定端口和IP,并监听

        将端口、IP和我们创建类传递到Baseserver类中;

      此时,对象的初始化工作并没有完成,接着,我们要进入baseserver类,看看该类下的__init__完成了什么工作:

    class BaseServer:
        timeout = None
        def __init__(self, server_address, RequestHandlerClass):
            self.server_address = server_address #将端口和IP暂存
            self.RequestHandlerClass = RequestHandlerClass  #暂存我们创建的类
            self.__is_shut_down = threading.Event() # 创建event对象

      到此,对象的初始化工作完成。然后是调用serve_forever()方法,开始不断循环监听。下面,我们来看看,这个server_forever实现

      注意:我们要清楚一点,我们在找這个方法在哪里的时候,一定要按照顺序去找,也就是说,我们先得从子类开始找,如果子类不存在,就去其父类找。下面我们就遵循這个原则来找找看。

      先来看看左继承的父类ThreadingMixIn中有没有server_forever:

    复制代码
    class ThreadingMixIn:
        daemon_threads = False
    
        def process_request_thread(self, request, client_address):
            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):
            t = threading.Thread(target = self.process_request_thread,
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()
    复制代码

      再来看看父类Tcpserver:

    复制代码
    class TCPServer(BaseServer):def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
          
        def server_bind(self):
            def server_activate(self):
           def server_close(self):
    def fileno(self):
     def get_request(self):
     def shutdown_request(self, request):
       def close_request(self, request):
        
    复制代码

      我们发现,没有server_forever方法,好,我去其继承的父类BaseServer类看看:

    复制代码
    class BaseServer:def __init__(self, server_address, RequestHandlerClass):
            def server_activate(self):
           
        def serve_forever(self, poll_interval=0.5):
    def shutdown(self):
            def service_actions(self):
            
        def handle_request(self):
          def _handle_request_noblock(self):
           def handle_timeout(self):
            
        def verify_request(self, request, client_address):
            def process_request(self, request, client_address):
         def server_close(self):
          
        def finish_request(self, request, client_address):
        def shutdown_request(self, request):
         def close_request(self, request):
     
        def handle_error(self, request, client_address):
    def __enter__(self):
    def __exit__(self, *args):
    
    复制代码

      我们发现server_forever()果然在這个类中,现在,我们的目标是:找到在什么地方调用我们自己写的handle方法。

      在我们找到的server_forever()方法中,

    复制代码
     def serve_forever(self, poll_interval=0.5):
            self.__is_shut_down.clear()
            try:
                with _ServerSelector() as selector:
                    selector.register(self, selectors.EVENT_READ)#原来底层是用epoll来实现不断循环监听
                    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 _handle_request_noblock(self):
            try:
                request, client_address = self.get_request()
            except OSError:
                return
            if self.verify_request(request, client_address):
                try:
                    self.process_request(request, client_address)#注意这里的process_request()
                except Exception:
                    self.handle_error(request, client_address)
                    self.shutdown_request(request)
                except:
                    self.shutdown_request(request)
                    raise
            else:
                self.shutdown_request(request)
    复制代码

      到源码中,我们找到该函数,现在,只看我划线的部分。其他部分都是针对异常的处理,如果没有异常,其他都是不会执行的,所以,其他的异常处理,我们先暂时不看。

      我们发现,如果有链接,最后会交给process_request()(我们会发现,在baseserver类和ThreadingMixIn都有這个方法,这里找类方法,一定要按照类的继承顺序来查找),所以,我们到ThreadingMiXin中去看看processs_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_requsest_thread()方法,

    复制代码
        def process_request_thread(self, request, client_address):
            try:
                self.finish_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)
    复制代码

      到此为止,链接建立成功!

      下面,我们来看看,当有消息发送,是如何进行处理的。

      当有消息发送,selector监听到了,

    复制代码
        def serve_forever(self, poll_interval=0.5):
            self.__is_shut_down.clear()
            try:
            
                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()
    复制代码

      下面我们跟进_handle_request_noblock(),

    复制代码
        def _handle_request_noblock(self):
            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)
    复制代码

      我们到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,  # start a threading to handle the request
                                 args = (request, client_address))
            t.daemon = self.daemon_threads
            t.start()
    复制代码

      然后开启线程执行,process_request_thread()方法,

    复制代码
        def process_request_thread(self, request, client_address):
          
            try:
                self.finish_request(request, client_address) # -----> to Baseserver find
            except Exception:
                self.handle_error(request, client_address)
            finally:
                self.shutdown_request(request)
    复制代码

      然后调用finish_request()方法,现在我们跟进看看,

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

      执行了RequestHandlerClass(request, client_address, self),這个是啥??还记得最开始我们传进来的类保存在哪呢?没错,就是RequestHandlerClass里面,现在这里才开始实例化這个类,也就是说,在这里开始调用我们自己的类了。既然是调用我们自己的类,那么必然要实例化,我们先回到自己创建的类,找找__init__方法。

    class MyTCPHandler(socketserver.BaseRequestHandler):
        def handle(self):
            self.data = self.request.recv(1024).strip()
            print("{} wrote:".format(self.client_address[0]))
            print(self.data)
            self.request.sendall(self.data.upper())

      自己类没有写__init__方法,那么我去它继承的BaseRequestHandler()下面找找看:

    复制代码
    class BaseRequestHandler:
        def __init__(self, request, client_address, server):
            self.request = request # 接受传进来的请求链接
            self.client_address = client_address  # 客户端的ip/端口
            self.server = server  # 
            self.setup()
            try:
                self.handle()
            finally:
                self.finish()
    
        def setup(self):
            pass
    
        def handle(self):
            pass
    
        def finish(self):
            pass
    复制代码

      我们来看看,它继承类实例化完成了哪些操作:

        调用handle()方法,我们发现,在这个类中也有一个handle()方法,那么这里调用时调用自己写的还是這个类中的呢?

      当然是调用我们自己写!

      至此,我们完成了一次通信的完整过程!

      总结sockerserver整个流程:

        1.开启了线程,支持并发操作

        2.I/O多路复用,监听多个文件描述符!

     

    参考

     
  • 相关阅读:
    新的一年,来看看大数据与AI的未来展望
    看过上百部片子的这个人教你视频标签算法解析
    让老板虎躯一震的前端技术,KPI杀手
    如何用RSS订阅?
    说说不知道的Golang中参数传递
    我也要谈谈大型网站架构之系列(3)——死了都要说的缓存
    我也要谈谈大型网站架构之系列(2)——纵观历史演变(下)
    我也要谈谈大型网站架构之系列(1)——纵观历史演变(上)
    抛弃NVelocity,来玩玩Razor
    挖一挖C#中那些我们不常用的东西之系列(3)——StackTrace,Trim
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/9266117.html
Copyright © 2011-2022 走看看