zoukankan      html  css  js  c++  java
  • socketserver模块

      虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Python标准库中很多服务器框架的基础

      socketserver模块可以简化网络服务器的编写,Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。

    一、单线程示例

      server端和客户端持续聊天的示例,但是同时只能和一个客户端进行通信,并没有并发的效果。

    1、服务端代码

    from socket import *
    ip_port=('127.0.0.1',8880)
    
    tcp_socket_server=socket()
    tcp_socket_server.bind(ip_port)
    tcp_socket_server.listen(5)
    
    while 1:
    
        conn,addr=tcp_socket_server.accept()
        print('客户端',addr)
        while 1:
            client_data=conn.recv(1024)
            if len(client_data)==0:
                  print("客户端断开连接,等待新的用户连接....")
                  break
            print ("接受数据 >>>",str(client_data,"utf8"))
            response=input("响应数据 >>>")
            conn.sendall(bytes(response,"utf8"))
    
        conn.close()
    

    2、客户端代码

    import socket
    ip_port = ('127.0.0.1',8888)
    sock = socket.socket()
    sock.connect(ip_port)
    print ("客户端启动:")
    
    while True:
    
        inp = input('发送数据 >>>')
        if inp == 'exit':
            break
        sock.sendall(bytes(inp,"utf8"))
        server_response=sock.recv(1024)
        print ("服务端响应数据 >>>",str(server_response,"utf8"))
    
    sock.close()
    

    二、socketserver的使用模式

    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
        def handle(self):
            """
            并发的业务逻辑
            conn(客户端套接字对象):self.request
            :return:
            """
            while 1:
                client_data=self.request.recv(1024)
                if client_data.decode("utf8") == "exit":
                      print("客户端断开连接,等待新的用户连接....")
                      break
                print("接受数据 >>>",str(client_data, "utf8"))
                response=input("响应数据 >>>")
                self.request.sendall(bytes(response, "utf8"))
    
            self.request.close()
    
    # 封装TCP协议相关的套接字对象
    server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一个参数是ip+port,第二个参数是类名MyServer
    
    server.serve_forever()

    1、总结socketserver使用分以下三步:

    (1)自定义功能类

    class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
        def handle(self):
            """
            并发的业务逻辑
            conn(客户端套接字对象):self.request
            :return:
            """
            while 1:
                client_data=self.request.recv(1024)
                if len(client_data)==0:
                      print("客户端断开连接,等待新的用户连接....")
                      break
                print ("接受数据 >>>",str(client_data,"utf8"))
                response=input("响应数据 >>>")
                self.request.sendall(bytes(response,"utf8"))
    
            self.request.close()
    

    (2)封装TCP协议相关的套接字对象

    server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一个参数是ip+port,第二个参数是类名MyServer
    

    (3)调用server.serve_forever()

    server.serve_forever()
    

    2、运行客户端服务端显示效果

            

    三、socketserver源码解析

    1、socketserver.ThreadingTCPServer源码解析

    (1)查看ThreadingTCPServer类

    class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
    

      在socketserver.py中查看到这句源码,混合继承了ThreadingMixIn和TCPServer类。根据广度优先先查看左边的类。

    (2)查看ThreadingMixIn类

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

      发现这个类并没有__init__方法,只有两个实例方法。因此再回去查看TCPServer类。

    (3)查看TCPServer类中的__init__方法

    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):
            """
            构造函数
            :param server_address: 接收的要绑定的元组('127.0.0.1', 8880)
            :param RequestHandlerClass: 接收功能类MyServer
            :param bind_and_activate:
            """
            BaseServer.__init__(self, server_address, RequestHandlerClass)   # 执行BaseServer的__init__方法
            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
        """后面代码省略"""
    

      注意__init__方法接收的参数,server_address: 接收的要绑定的元组('127.0.0.1', 8880);RequestHandlerClass: 接收功能类MyServer。

      __init__方法第一步就是执行BaseServer的__init__方法。

    (4)查看BaseServer的__init__方法

    class BaseServer:
        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
    
        """代码省略"""
    

      可以看到主要是做了一个实例变量赋值

    (5)继续解析TCPServer类中的__init__方法

      在执行完BaseServer的__init__方法后,接下里开始创建socket对象:

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

      socket对象创建完成后,执行socket.bind和socket.listen方法:

    class TCPServer(BaseServer):
        def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
            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)   # bind元组地址
            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)   # listen,这里默认值也是5
    

    (6)总结server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer) 做的事情

      1.self.socket  2.self.socket.bind()  3.self.socket.listen(5) 

      这三步完成后,初始化也就完成了。

    2、server.serve_forever()源码解析

      由于server是ThreadingTCPServer的实例对象,因此依次追溯ThreadingTCPServer、ThreadingMixIn、TCPServer、BaseServer类,最终在BaseServer类中找到serve_forever方法。

    (1)查看serve_forever方法

    class BaseServer:
        def serve_forever(self, poll_interval=0.5):
            self.__is_shut_down.clear()
            try:
                with _ServerSelector() as selector:   # IO多路复用监听
                    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()
    

      前面两步都是监听相关的操作,直接查看self._handle_request_noblock()源码。

    (2)查看_handle_request_noblock方法

      同样是依次追溯各个类,最后在BaseServer类中找到方法:

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

    (3)查看self.get_request()方法

    class TCPServer(BaseServer):
    
        def get_request(self):
            """Get the request and client address from the socket.
            May be overridden.
            """
            return self.socket.accept()
    

      在这里找到了socket.accept方法,被动接受TCP客户的连接,(阻塞式)等待连接到来。

      因此_handle_request_noblock中的request, client_address = self.get_request():request就是新的套接字对象(conn),可以用来接收和发送数据;client_address就是连接客户端的地址

      继续查看_handle_request_noblock方法的self.process_request(request, client_address)调用。

    (4)查看process_request方法

      这里按照查找顺序,在ThreadingMixIn类找到process_request方法。(注意不要找BaseServer里的process_request,它已经被覆盖了)

    class ThreadingMixIn:
    
        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()
    

      每来一个用户就开启一个线程,执行self.process_request_thread(request, client_address)方法。

      process_request_thread方法也在ThreadingMixIn类中。

    (5)查看process_request_thread方法

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

      按照查找顺序在BaseServer中找到finish_request方法。

    (6)查看finish_request方法

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

      注意这里的RequestHandlerClass在TCPServer类中的__init__方法执行时,用RequestHandlerClass来接收功能类MyServer。

      这里是在调用MyServer类的实例化。

    class MyServer(socketserver.BaseRequestHandler):    # 必须继承这个类
        def handle(self):
            """
            并发的业务逻辑
            conn(客户端套接字对象):self.request
            :return:
            """
            while 1:
                client_data=self.request.recv(1024)
                if client_data.decode("utf8") == "exit":
                      print("客户端断开连接,等待新的用户连接....")
                      break
                print("接受数据 >>>",str(client_data, "utf8"))
                response=input("响应数据 >>>")
                self.request.sendall(bytes(response, "utf8"))
    
            self.request.close()
    

      但是这个类并没有__init__方法,因此需要查看它的父类__init__方法。

    (7)查看BaseRequestHandler类

    class BaseRequestHandler:
    
        def __init__(self, request, client_address, server):
            self.request = request     # 新的套接字对象(conn)实例变量赋值
            self.client_address = client_address    # 连接客户端的地址实例变量赋值
            self.server = server
            self.setup()
            try:
                self.handle()  # 调用自己的handle方法
            finally:
                self.finish()
    

      在完成新的套接字对象(conn)实例变量赋值和连接客户端的地址实例变量赋值后,调用自己的handle方法。

      这个self.handle()调用的不是BaseRequestHandler类的handle方法而是MyServer的handle方法,执行自定义的handle方法

    3、流程图

      

  • 相关阅读:
    【Linux】VMware及VirtualBox网络配置
    【Linux】VirtualBox安装ubuntu排错LowGraphic
    【Hadoop】Win7上搭建Hadoop开发环境,方法一
    【JAVA】配置JAVA环境变量,安装Eclipse
    Eureka自我保护机制
    zookeeper代替eureka作为SpringCloud的服务注册中心
    mybatisplus代码生成器
    条件构造器 EntityWrapper (重要)
    idea 常用快捷键
    MybatisPlus的通用 CRUD
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9710964.html
Copyright © 2011-2022 走看看