• day29


    一、socketserver(并发)

       TCP协议下的socket一次只能和一个客户端通信,如果用socketserver模块则可以实现和多个客户端通信.它是在socket的基础上进行了一次封装,底层仍然调用的是socket.

      通过这两天学习socket套接字,我们发现在写服务端和客户端的时候,在建立连接之前总是要写一些固定的重复代码来,比如socket.socket()(创建套接字对象)、bind()、acecept()等等,学习了socketserver(并发)之后,就可以少写一些代码,并且实现并发,如下示例:

        import socketserver 
    
        class Myserver(socketserver.BaseRequestHandler):
            def handle(self): 
                """
              到此已经是等待跟客户端连接的状态
              所以从这里写代码正常逻辑代码
              conn 用self.request替换即可
                """
    
        # 1.创建socket对象   2.self.socket.bind()    3 .self.socket.listen(5)
        server = socketserver.ThreadingTCPServer(('127.0.0.1',8800),Myserver)
        
        # 1.self.accept()  2.执行了自定义类中的handle方法
        server.serve_forever()  

      创建一个并发服务端的流程:

        (1) 导入scoketserver模块

        (2) 定义一个自定义类, 类中必须定义一个handle方法,这个方法里面写正常的逻辑代码.

        (3) 实例化scoketserver模块中的ThreadingTCPServer的对象,传入两个参数(地址元组, 自定义类)

    二、socketserver源码分析

      结合下图中类的继承关系和类中的方法,分析socketserver代码的执行过程:

      a、初始化相关过程:server = socketserver.ThreadingTCPServer(('127.0.0.1',8800),Myserver)

        (1) TCPServer类中的__init__方法:

                         TCPServer类中主动执行BaseServer类中的__init__ 方法(把自己创建的Myserver类传参)

                         创建socket.socket()对象;

                         server_bind() -- 在TCPServer类中执行了socket.bind(self.server_address);

                         server_activate() -- 在TCPServer类中执行了socket.listen(5);


        (2) BaseServer类中的__init__ 方法:

          将参数server_address(ip地址和端口号)赋值给了self.server_address;

                         将形参RequestHandlerClass(实参是我们自己创建的Myserver类)赋值给了self.RequestHandlerClass;

          

      b、执行server.serve_forever()

        (1) 执行BaseServer类中的serve_forever()方法:

          

          注意看if ready后边的那句self._handle_request_noblock(),其他先忽略;

        (2) 执行BaseServer中的_handle_request_noblock(self)方法:

            

          看第一个try中request,client_address = self.get_request(),

            TCPServer中有get_request()方法,方法中是return self.socket.accept(),即等待连接;

          再看第二个try中的self.process_request(request,client_address)

        (3) 连接成功之后拿到了request(即conn)和client_address(即addr)再去执行ThreadingMixIn类中的.process_request方法;

          

          t = threading.Thread(target = self.process_request_thread, args = (request, client_address))

          主要看上面这句, 意思是创建线程,执行方法process_request_thread(request, client_address)

        (4) 执行ThreadingMixIn 类中的process_request_thread(self, request, client_address)方法 :

          

           看try中self.finish_request(request,client_address)

        (5) 执行BaseServer中的finish_request(request,client_address)方法:

          

           self.RequestHandlerClass这个参数就是我们执行BaseServer中__init__方法时传过来的自己创建的类Myserver, 类() 即实例化一个Myserver对象,并且传了两个参数(conn,addr), 但是我们自己写的Myserver类中没有__init__方法,所以执行Myserver父类BaseRequestHandler类中的__init__方法,并且带了每个连接的线程的conn和addr:

        (6) 执行BaseRequestHandler中的__init__方法:

          

           此时self是Myserver类的对象,所以优先去执行Myserver类中handle方法。

      

      附录:以下是各类的继承关系以及类中成员方法:

      

      

       

       

      

      

  • 相关阅读:
    《国富论》
    DataGridView
    《ASP.NET Core 3框架揭秘》
    看见
    英语常用词汇
    《未选择的路》
    Redis实战(20)Redis 如何从海量数据中查询出某一个 Key?
    .NET 程序集Assembly使用
    ExtJs基础知识总结:自定义弹窗和ComboBox自动联想加载(四)
    ExtJs基础知识总结:Dom、IFrame和TreePanel、TabPanel(三)
  • 原文地址:https://www.cnblogs.com/kangqi452/p/11681125.html
Copyright © 2020-2023  润新知