解读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多路复用,监听多个文件描述符!
解读socketserver之Tcpserver的更多相关文章
- Python面试题之解读Socketserver & Tcpserver
在解析socketserver是如工作之前,我们先看看socektserver类的继承关系图: 请求类继承关系: server类继承关系: 有了上面的继承关系图后,我们解析socketserver就轻 ...
- 文件上传下载、socketserver(并发)、解读socketserver源码
1.文件上传/下载 学习了socket套接字,我们现在可以写一个文件上传/下载的程序,如下示例: 分析上边代码,我们发现,client发送上传文件相关信息的字典序列化之后,server又给client ...
- 解读socketserver源码
解读python中SocketServer源码 再看继承 真正的大餐来之前,还是来点儿开胃菜!回顾一下关于类的继承的知识: 我们先看上面的代码,这是一个简单的类继承,我们可以看到父类Base和子 ...
- python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)
先来回顾一下昨天的内容 黏包现象粘包现象的成因 : tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制 无边界 所有在连接建立的基础上传递的数据之间没有界限 收发消息很有可能不完全相 ...
- python- 粘包 struct,socketserver
黏包 黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) res=subprocess.Popen(cmd.decode('utf-8'), sh ...
- python socket和socketserver
Python提供了两个基本的socket模块.一个是socket,它提供了标准的BSD Socket API:另一个是socketServer,它提供了服务器中心类,可以简化网络服务器的开发. 下面先 ...
- Python网络编程03----Python3.*中socketserver
socketserver(在Python2.*中的是SocketServer模块)是标准库中一个高级别的模块.用于简化网络客户与服务器的实现(在前面使用socket的过程中,我们先设置了socket的 ...
- Python——socketserver编程(客户端/服务器)
一.socketserver是标准库中的高级模块,它的目标是简化很多多样板代码,是创建网络客户端和服务器所必须的代码.(事件驱动) 二.模块类 BaseServer :包含核心服务器功能和mix-in ...
- python自动化开发-[第十天]-线程、协程、socketserver
今日概要 1.线程 2.协程 3.socketserver 4.基于udp的socket(见第八节) 一.线程 1.threading模块 第一种方法:实例化 import threading imp ...
随机推荐
- hdu 1716 排列2(DFS搜索)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1716 排列2 Time Limit: 1000/1000 MS (Java/Others) Me ...
- 二、springcloud之熔断器hystrix
一.背景 雪崩效应 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应.服务雪崩效应是一种因“服务提供者”的不可用导致“服 ...
- IP地址、域名、域名解析系统相关
IP地址(Internet Protocol Address) 它来自TCP/IP协议,存在于其中的IP层,用于实现不同计算机之间的通信,类似于门牌号. 设计之处,IP地址是准备给地球上每一台计算机一 ...
- html-表格和列表
一:表格标签 表格 描述 <table> 定义表格 <caption> 定义表格标题. <th> 定义表格的表头. <tr> 定义表格的行. <t ...
- 洛谷P1342请柬
传送门啦 核心思想:两遍最短路. 1号点去各地的时间直接套最短路模板,各地到1号点时间用逆向思维,视为求1号点沿反边到各地的时间即可. #include <iostream> #inclu ...
- AdvStringGrid 列宽度、列移动、行高度、自动调节
那么有没有办法,让客户自己去调整列的宽度呢? 那么有没有办法 让列宽度.行高度 随着内容而自动变换呢: unit Unit5; interface uses Winapi.Windows, Winap ...
- MAC下安装MAMP后,mysql server无法启动
用MAC下载安装了MAMP,之前使用是很好没问题的,但是突然无法启动mysql server,检查日志,提示InnDB出错,然后删掉了/Application/MAMP/db/mysql56目录下的i ...
- CCF CSP 201604-3 路径解析
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201604-3 路径解析 问题描述 在操作系统中,数据通常以文件的形式存储在文件系统中.文件系 ...
- OS X 配置 Apache
1.去掉javascript:void(0);Include /private/etc/apache2/extra/httpd-vhosts.con的注释,以启用虚拟主机: 2.在<Direct ...
- C语言:10个整数排序(别忘了负数)
题目内容: 10个整数排序(别忘了负数) 例如 input 1 0 2 0 3 4 1 9 8 7 output 0 0 1 1 2 3 4 7 8 9 编码: void sort(int *a); ...