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

  请求类继承关系:

          

  server类继承关系:

          

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

  1. import socketserver
  2. import struct, json, os
  3.  
  4. class FtpServer(socketserver.BaseRequestHandler):
  5. coding = 'utf-8'
  6. server_dir = 'file_upload'
  7. max_packet_size = 1024
  8. BASE_DIR = os.path.dirname(os.path.abspath(__file__))
  9.  
  10. def handle(self):
  11. print(self.request)
  12. while True:
  13. data = self.request.recv(4)
  14. data_len = struct.unpack('i', data)[0]
  15. head_json = self.request.recv(data_len).decode(self.coding)
  16. head_dic = json.loads(head_json)
  17. cmd = head_dic['cmd']
  18. if hasattr(self, cmd):
  19. func = getattr(self, cmd)
  20. func(head_dic)
  21.  
  22. def put(self):
  23. pass
  24.  
  25. def get(self):
  26. pass
  27.  
  28. if __name__ == '__main__':
  29. HOST, PORT = "localhost", 9999
  30. with socketserver.ThreadingTCPServer((HOST, PORT), FtpServer) as server:
  31. server.serve_forever()

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

  1. class ThreadingTCPServer(ThreadingMixIn, UDPServer): pass

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

  1. class ThreadingMixIn:
  2. daemon_threads = False
  3.  
  4. def process_request_thread(self, request, client_address):
  5. passdef process_request(self, request, client_address):
  6. pass

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

  1. class TCPServer(BaseServer):
  2. address_family = socket.AF_INET
  3. socket_type = socket.SOCK_STREAM
  4. request_queue_size = 5
  5. allow_reuse_address = False
  6. def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
  7. BaseServer.__init__(self, server_address, RequestHandlerClass)#
  8. self.socket = socket.socket(self.address_family,self.socket_type) # 创建套接字对象
  9. if bind_and_activate:
  10. try:
  11. self.server_bind() #绑定端口和IP
  12. self.server_activate() # 监听端口
  13. except:
  14. self.server_close()
  15. raise

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

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

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

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

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

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

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

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

  1. class ThreadingMixIn:
  2. daemon_threads = False
  3.  
  4. def process_request_thread(self, request, client_address):
  5. try:
  6. self.finish_request(request, client_address)
  7. except Exception:
  8. self.handle_error(request, client_address)
  9. finally:
  10. self.shutdown_request(request)
  11.  
  12. def process_request(self, request, client_address):
  13. t = threading.Thread(target = self.process_request_thread,
  14. args = (request, client_address))
  15. t.daemon = self.daemon_threads
  16. t.start()

  再来看看父类Tcpserver:

  1. class TCPServer(BaseServer):def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
  2.  
  3. def server_bind(self):
  4. def server_activate(self):
  5. def server_close(self):
  6. def fileno(self):
  7. def get_request(self):
  8. def shutdown_request(self, request):
  9. def close_request(self, request):

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

  1. class BaseServer:def __init__(self, server_address, RequestHandlerClass):
  2. def server_activate(self):
  3.  
  4. def serve_forever(self, poll_interval=0.5):
  5. def shutdown(self):
  6. def service_actions(self):
  7.  
  8. def handle_request(self):
  9. def _handle_request_noblock(self):
  10. def handle_timeout(self):
  11.  
  12. def verify_request(self, request, client_address):
  13. def process_request(self, request, client_address):
  14. def server_close(self):
  15.  
  16. def finish_request(self, request, client_address):
  17. def shutdown_request(self, request):
  18. def close_request(self, request):
  19.  
  20. def handle_error(self, request, client_address):
  21. def __enter__(self):
  22. def __exit__(self, *args):

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

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

  1. def serve_forever(self, poll_interval=0.5):
  2. self.__is_shut_down.clear()
  3. try:
  4. with _ServerSelector() as selector:
  5. selector.register(self, selectors.EVENT_READ)#原来底层是用epoll来实现不断循环监听
  6. while not self.__shutdown_request:
  7. ready = selector.select(poll_interval) #有新的链接进来
  8. if ready:
  9. self._handle_request_noblock() # 这里应该是处理新的链接
  10. self.service_actions()
  11. finally:
  12. self.__shutdown_request = False
  13. self.__is_shut_down.set()

  好,我大致找到了链接的处理入口,我们跟进去,继续寻找:

  1. def _handle_request_noblock(self):
  2. try:
  3. request, client_address = self.get_request()
  4. except OSError:
  5. return
  6. if self.verify_request(request, client_address):
  7. try:
  8. self.process_request(request, client_address)#注意这里的process_request()
  9. except Exception:
  10. self.handle_error(request, client_address)
  11. self.shutdown_request(request)
  12. except:
  13. self.shutdown_request(request)
  14. raise
  15. else:
  16. self.shutdown_request(request)

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

  我们发现,如果有链接,最后会交给process_request()(我们会发现,在baseserver类和ThreadingMixIn都有這个方法,这里找类方法,一定要按照类的继承顺序来查找),所以,我们到ThreadingMiXin中去看看processs_request()做了哪些事情:

  1. def process_request(self, request, client_address):
  2.  
  3. t = threading.Thread(target = self.process_request_thread,args = (request, client_address)) # 原来开了一个线程,支持并发
  4. t.daemon = self.daemon_threads # 开启守护线程
  5. t.start()

  在线程中执行该类下的process_requsest_thread()方法,

  1. def process_request_thread(self, request, client_address):
  2. try:
  3. self.finish_request(request, client_address)
  4. except Exception:
  5. self.handle_error(request, client_address)
  6. finally:
  7. self.shutdown_request(request)

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

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

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

  1. def serve_forever(self, poll_interval=0.5):
  2. self.__is_shut_down.clear()
  3. try:
  4.  
  5. with _ServerSelector() as selector:
  6. selector.register(self, selectors.EVENT_READ)# 监听了活动链接
  7.  
  8. while not self.__shutdown_request:
  9. ready = selector.select(poll_interval)
  10. if ready: # 准备好了
  11. self._handle_request_noblock() # 进入处理
  12.  
  13. self.service_actions()
  14. finally:
  15. self.__shutdown_request = False
  16. self.__is_shut_down.set()

  下面我们跟进_handle_request_noblock(),

  1. def _handle_request_noblock(self):
  2. try:
  3. request, client_address = self.get_request()
  4. except OSError:
  5. return
  6. if self.verify_request(request, client_address):
  7. try:
  8. self.process_request(request, client_address)
  9. except Exception:
  10. self.handle_error(request, client_address)
  11. self.shutdown_request(request)
  12. except:
  13. self.shutdown_request(request)
  14. raise
  15. else:
  16. self.shutdown_request(request)

  我们到process_request()看看:

  1. def process_request(self, request, client_address):
  2. """Start a new thread to process the request."""
  3. t = threading.Thread(target = self.process_request_thread, # start a threading to handle the request
  4. args = (request, client_address))
  5. t.daemon = self.daemon_threads
  6. t.start()

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

  1. def process_request_thread(self, request, client_address):
  2.  
  3. try:
  4. self.finish_request(request, client_address) # -----> to Baseserver find
  5. except Exception:
  6. self.handle_error(request, client_address)
  7. finally:
  8. self.shutdown_request(request)

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

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

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

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

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

  1. class BaseRequestHandler:
  2. def __init__(self, request, client_address, server):
  3. self.request = request # 接受传进来的请求链接
  4. self.client_address = client_address # 客户端的ip/端口
  5. self.server = server #
  6. self.setup()
  7. try:
  8. self.handle()
  9. finally:
  10. self.finish()
  11.  
  12. def setup(self):
  13. pass
  14.  
  15. def handle(self):
  16. pass
  17.  
  18. def finish(self):
  19. pass

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

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

  当然是调用我们自己写!

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

  总结sockerserver整个流程:

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

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

解读socketserver之Tcpserver的更多相关文章

  1. Python面试题之解读Socketserver & Tcpserver

    在解析socketserver是如工作之前,我们先看看socektserver类的继承关系图: 请求类继承关系: server类继承关系: 有了上面的继承关系图后,我们解析socketserver就轻 ...

  2. 文件上传下载、socketserver(并发)、解读socketserver源码

    1.文件上传/下载 学习了socket套接字,我们现在可以写一个文件上传/下载的程序,如下示例: 分析上边代码,我们发现,client发送上传文件相关信息的字典序列化之后,server又给client ...

  3. 解读socketserver源码

    解读python中SocketServer源码 再看继承 真正的大餐来之前,还是来点儿开胃菜!回顾一下关于类的继承的知识:    我们先看上面的代码,这是一个简单的类继承,我们可以看到父类Base和子 ...

  4. python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)

     先来回顾一下昨天的内容 黏包现象粘包现象的成因 : tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制 无边界 所有在连接建立的基础上传递的数据之间没有界限 收发消息很有可能不完全相 ...

  5. python- 粘包 struct,socketserver

    黏包 黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) res=subprocess.Popen(cmd.decode('utf-8'), sh ...

  6. python socket和socketserver

    Python提供了两个基本的socket模块.一个是socket,它提供了标准的BSD Socket API:另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发. 下面先 ...

  7. Python网络编程03----Python3.*中socketserver

    socketserver(在Python2.*中的是SocketServer模块)是标准库中一个高级别的模块.用于简化网络客户与服务器的实现(在前面使用socket的过程中,我们先设置了socket的 ...

  8. Python——socketserver编程(客户端/服务器)

    一.socketserver是标准库中的高级模块,它的目标是简化很多多样板代码,是创建网络客户端和服务器所必须的代码.(事件驱动) 二.模块类 BaseServer :包含核心服务器功能和mix-in ...

  9. python自动化开发-[第十天]-线程、协程、socketserver

    今日概要 1.线程 2.协程 3.socketserver 4.基于udp的socket(见第八节) 一.线程 1.threading模块 第一种方法:实例化 import threading imp ...

随机推荐

  1. 【leetcode 简单】 第一百零六题 压缩字符串

    给定一组字符,使用原地算法将其压缩. 压缩后的长度必须始终小于或等于原数组长度. 数组的每个元素应该是长度为1 的字符(不是 int 整数类型). 在完成原地修改输入数组后,返回数组的新长度. 进阶: ...

  2. 【leetcode 简单】 第七十一题 二叉树的所有路径

    给定一个二叉树,返回所有从根节点到叶子节点的路径. 说明: 叶子节点是指没有子节点的节点. 示例: 输入: 1 / \ 2 3 \ 5 输出: ["1->2->5", ...

  3. Request爬取网站(seo.chinaz.com)百度权重的查询结果

    一:脚本需求 利用Python3查询网站权重并自动存储在本地数据库(Mysql数据库)中,同时导出一份网站权重查询结果的EXCEL表格 数据库类型:MySql 数据库表单名称:website_weig ...

  4. 初时Python博大精深

    Python是解释型语言 编译型vs解释型 编译型优点:编译器一般会有预编译的过程对代码进行优化.因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高.可以脱离语言环境独立运行.缺点:编 ...

  5. jquery对不同id的按钮执行同一类型的操作

    不同id执行相同操作: $("#id1,#id2,#id3,#id4") 获取相同class的text值: $(".className").each(funct ...

  6. Fiddler大师之路系列(一)

    江湖传言,Fiddler是捕获客户端与服务器之间的所有HTTP(S) 请求的利器,但是在具体使用过程中,发现使用Fiddler进行抓包时有一部分请求总是没到,多方苦寻之下发现客户端使用WinINET这 ...

  7. 【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树

    不得不说,做过最爽的树套树———— 由于有了区间操作,我们很容易把区间看成一棵平衡树,对他进行插入,那么外面一层就是平衡树了,这就与我们之前所见到的不同了.我们之前所见到的大多数是线段树套平衡树而此题 ...

  8. django Rest Framework----APIView 执行流程 APIView 源码分析

    在django—CBV源码分析中,我们是分析的from django.views import View下的执行流程,这篇博客我们介绍django Rest Framework下的APIView的源码 ...

  9. Python爬虫之三种网页抓取方法性能比较

    下面我们将介绍三种抓取网页数据的方法,首先是正则表达式,然后是流行的 BeautifulSoup 模块,最后是强大的 lxml 模块. 1. 正则表达式   如果你对正则表达式还不熟悉,或是需要一些提 ...

  10. Python_oldboy_自动化运维之路_函数,装饰器,模块,包(六)

    本节内容 上节内容回顾(函数) 装饰器 模块 包 1.上节内容回顾(函数) 函数 1.为什么要用函数? 使用函数之模块化程序设计,定义一个函数就相当于定义了一个工具,需要用的话直接拿过来调用.不使用模 ...