zoukankan      html  css  js  c++  java
  • Python之socketserver源码分析

    一、socketserver简介

      socketserver是一个创建服务器的框架,封装了许多功能用来处理来自客户端的请求,简化了自己写服务端代码。比如说对于基本的套接字服务器(socket-based servers),里面就定义了地址族(AF_INET,AF_UNIX等)、套接字类型(SOCK_STREAM,SOCK_DGRAM)等,此外对于基于请求的服务器(request-based servers),里面就详细叙述了如何处理客户端认证,多重请求,以及如何实现多线程多进程等。 

    二、server种类

      在serketserver里有5种类型的server,他们的继承关系如下图,下面的4个类都是同步处理请求的,不支持异步处理,同步处理意思是下一个请求必须等待上一个请求被处理完才能开始。如果要让他们支持异步处理,需要继承ForkingMixIn or ThreadingMixIn,使他们支持继承多进程和多线程。比如说定义一个多线程UDPserver需要这样写 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

      

      如果要自己写一个server类,首先要继承基本请求处理类baseRequestHandler,然后重写他的 handle()方法来实现你自己的请求处理,需要注意的是要区分开StreamRequestHandler or DatagramRequestHandler(TCp or UDP)

    1.BaseServer

      BaseServer是其他几种server类的基类,类中定义的方法有下面这些:

    Methods for the caller:
    
        - __init__(server_address, RequestHandlerClass)         #初始化 可扩长
        - serve_forever(poll_interval=0.5)                #被构造函数调用激活服务器
        - shutdown()                             #关闭server_forever循环
        - handle_request()  # if you do not use serve_forever()     #请求处理函数,可能会被阻塞
                                          #get_request->verify_request->process_request
        - fileno() -> int   # for selector                          #返回一个整型文件操作符供服务器监听,
    
    Methods that may be overridden:
    
        - server_bind()                           #绑定到要监听的地址上
        - server_activate()                         #被构造函数调用激活服务器,
        - get_request() -> request, client_address           #获取请求
        - handle_timeout()                         #处理超时
        - verify_request(request, client_address)            #验证请求
        - server_close()                          #关闭服务器
        - process_request(request, client_address)           #进程处理,调用finish_request 完成请求
        - shutdown_request(request)                    #被shutdown调用,关闭一个单独的请求
        - close_request(request)                      #关闭请求
        - service_actions()                         #被serve_forever 调用
        - handle_error()                           #处理错误 

    2、TCPServer

      TCPServer是一种基本的套接字服务器类,采用TCP协议,继承自BaseServer,这里我会着重讲下TCPServer,然后自己写一个TCPserver,首先我们来看下在Socketserver源码中看下代码实现,

    class TCPServer(BaseServer):
    
        address_family = socket.AF_INET                        # 定义地址族,ipv4
        socket_type = socket.SOCK_STREAM                  # 定义套接字类型,流式套接字(TCP)
      request_queue_size = 5                        #定义监听的数量 allow_reuse_address = False                     # 定义是否要设置套接口的选项 def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
    BaseServer.__init__(self, server_address, RequestHandlerClass)       #调用BaseServer的初始化 self.socket = socket.socket(self.address_family,self.socket_type)      #创建一个套接字 if bind_and_activate: try: self.server_bind() self.server_activate()                    #调用bind()和activate() except:   self.server_close()                            #关闭server raise def server_bind(self):                                 #绑定监听地址 if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)   #设定socket self.socket.bind(self.server_address)                       #绑定地址 self.server_address = self.socket.getsockname()             #获取待连接客户端的地址 def server_activate(self):                              self.socket.listen(self.request_queue_size)                   #监听  def server_close(self): self.socket.close()                                 #关闭 def fileno(self): return self.socket.fileno()                            #文件描述符 def get_request(self): return self.socket.accept()                            #接受客户端的连接 def shutdown_request(self, request): try: request.shutdown(socket.SHUT_WR)                       #关闭之前先调用shutdown保证通信双方数据不丢失 except OSError: pass                                        #之后再调用close self.close_request(request) def close_request(self, request):                           #释放套接字的描述符,直接调用可能丢弃传输队列中的数据 request.close()

    3、BaseRequestHandler和StreamRequestHandler  

      如果自己要实现TCPServer,我们还要自己定义一个请求处理类,而且要继承BaseRequestHandler或者StreamRequestHandler,并且覆盖他的handle()处理函数,用来处理请求。下面看一下他们的定义:

    class BaseRequestHandler
        def __init__(self, request, client_address, server):         #初始化request,client_addres server
            self.request = request
            self.client_address = client_address
            self.server = server
            self.setup()                             #在handle()方法前调用,初始化所有需要的
            try:
                self.handle()
            finally:
                self.finish()
    
        def setup(self):
            pass
    
        def handle(self):                            #具体的处理请求函数,需要被覆盖
            pass
    
        def finish(self):                            #在handle()方法好后调用,清理工作
            pass
    
    
    class StreamRequestHandler(BaseRequestHandler):
    
        rbufsize = -1                                                 #为流式套接字定义rfile和wfile
        wbufsize = 0
    
        timeout = None
    
        disable_nagle_algorithm = False                            #当wbufsize !=0才会用到,避免太小的包 
    
        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)   #一个类似文件的对象,可以向客户端返回数据
    
        def finish(self):
            if not self.wfile.closed:                       #wfile关闭之前需要刷新
                try:
                    self.wfile.flush()
                except socket.error:
                    pass
            self.wfile.close()
            self.rfile.close()

      到这里我们就可以自己编写一个单线程server类来,按照自己的要求来处理请求了,下面写了一个最简单的客户端与服务端通信的例子,主要是为了方便大家好理解。代码实例如下,打开两个命令行工具,首先运行server.py,在另外一个命令行运行client.py,会看到服务端收到的数据为'hello,server',客户端收到的数据为'你好'。

    #server.py
    import socketserver
    class myTCPHandle(socketserver.StreamRequestHandler):         
        def handle(self):                             #定义自己的请求处理函数 
            print("connet from %s:%s" % self.client_address)         #打印客户端的地址
            self.data=self.rfile.readline()                   #读取客户端的传过来的一行数据
            print(str(self.data,encoding="utf-8"))
            self.wfile.write(bytes('你好',encoding='utf-8'))         #发送数据‘你好’给客户端
    if __name__=='__main__':
        address=('127.0.0.1',9999)
        server=socketserver.TCPServer(address,myTCPHandle)          #创建一个基于TCPServer的套接字
        server.serve_forever()                          #调用它的server_forever()函数处理一个请求直到它被shutdown
    #client.py import socket client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)         #创建套接字 client.connect(('127.0.0.1',9999))                     #要连接的地址和端口 data='hello,server '                             #发送的数据 作为行结束 data_bytes=bytes(data,encoding="utf-8")                  #转换成字节发送 client.send(data_bytes)                            #发送数据 data_recive=client.recv(1024)                        #接受数据 print(str(data_recive,encoding='utf-8'))                  #将接受的数据转换成字符串 client.close()                                 #关闭套接字

    4、ThreadingMixIn

    如果要实现以支持多线程的server,就需要继承ThreadingMixIn类,下面是源码

    class ThreadingMixIn:
    
        daemon_threads = False
    
        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)
    
        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()                                  #启动线程
    

    在这里对于ForkingMixIn(多线程)、UDPServer、DatagramRequestHandler(数据报套接字)等我就不再一一介绍,他们的原理和多进程以及上面介绍的TCPServer基本一样。最后我会用一个多线程的TCPserver的实例作为结束。

    三、实现一个ThreadingTCPServer

    # server.py
    import socketserver
    import threading
    
    class ThreadingTCPServer(socketserver.ThreadingMixIn,socketserver.TCPServer):  #继承ThreadingMixIn,使其支持多线程
        pass
    
    class myTCPHandle(socketserver.StreamRequestHandler):
        def handle(self):
            print("connet from %s:%s" % self.client_address)
            cur_thread=threading.current_thread()                                  #打印当前处理请求线程的名字
            self.data=self.rfile.readline()                        #读取客户端的数据
            print(cur_thread.name)
            print(str(self.data,encoding="utf-8"))
            self.wfile.write(bytes('你好',encoding='utf-8'))               #向客户端发送数据
    
    if __name__=='__main__':
        address=('127.0.0.1',9999)
        server=socketserver.ThreadingTCPServer(address,myTCPHandle)         #创建一个支持多线程的socket
        #server=socketserver.TCPServer(address,myTCPHandle)
        server.serve_forever()                               #调用server_forever()处理请求直到被shutdown
    '''
    服务端打印的数据如下:
      connect from 127.0.0.1:xxx
      Thread-x
      from client:x
      ...
    '''
    
    #client.py
    import socket
    for i in range(5):
    	k=socket.socket(socket.AF_INET,socket.SOCK_STREAM)                   #定义了5个套接字,连接服务端,并向他发送数据
    	k.connect(('127.0.0.1',9999))
    	senddata='from client:{}
    '.format(i+1)                 #向服务端发送数据
    	k.send(bytes(senddata,encoding='utf-8'))
    	data_recive=k.recv(1024)
    	print(str(data_recive,encoding='utf-8'))                 #打印从服务端收到的数据 你好
    	k.close()
    

    到这里我就实现了一个如何处理多线程的TCPServer,程序虽然简单,但是有助于大家理解socketserver,至于多进程的实现也类似,这里就不多叙述了,大家有兴趣的可以去实现以下。

      

  • 相关阅读:
    WINDOWPOS结构
    HTML 4.01 符号实体
    建立窗体时所响应的消息
    如何用正确的方法来写出质量好的软件的75条体会
    Onload加载多个方法函数
    UML中的一些概念
    aop应用范围
    如何成为“10倍效率”开发者
    网页引入taglibs
    XML编程—CRUD
  • 原文地址:https://www.cnblogs.com/Wxtrkbc/p/5459941.html
Copyright © 2011-2022 走看看