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 # 定义地址族,ipv4socket_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() #关闭serverraisedef server_bind(self): #绑定监听地址if self.allow_reuse_address:self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #设定socketself.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 #之后再调用closeself.close_request(request)def close_request(self, request): #释放套接字的描述符,直接调用可能丢弃传输队列中的数据request.close()
3、BaseRequestHandler和StreamRequestHandler
如果自己要实现TCPServer,我们还要自己定义一个请求处理类,而且要继承BaseRequestHandler或者StreamRequestHandler,并且覆盖他的handle()处理函数,用来处理请求。下面看一下他们的定义:
class BaseRequestHandlerdef __init__(self, request, client_address, server): #初始化request,client_addres serverself.request = requestself.client_address = client_addressself.server = serverself.setup() #在handle()方法前调用,初始化所有需要的try:self.handle()finally:self.finish()def setup(self):passdef handle(self): #具体的处理请求函数,需要被覆盖passdef finish(self): #在handle()方法好后调用,清理工作passclass StreamRequestHandler(BaseRequestHandler):rbufsize = -1 #为流式套接字定义rfile和wfilewbufsize = 0timeout = Nonedisable_nagle_algorithm = False #当wbufsize !=0才会用到,避免太小的包 def setup(self):self.connection = self.requestif 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:passself.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\n' #发送的数据 \n作为行结束
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 = Falsedef 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_threadst.start() #启动线程
在这里对于ForkingMixIn(多线程)、UDPServer、DatagramRequestHandler(数据报套接字)等我就不再一一介绍,他们的原理和多进程以及上面介绍的TCPServer基本一样。最后我会用一个多线程的TCPserver的实例作为结束。
三、实现一个ThreadingTCPServer
# server.py
import socketserver
import threadingclass ThreadingTCPServer(socketserver.ThreadingMixIn,socketserver.TCPServer): #继承ThreadingMixIn,使其支持多线程passclass 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:xxxThread-xfrom 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:{}\n'.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,至于多进程的实现也类似,这里就不多叙述了,大家有兴趣的可以去实现以下。
转载于:https://www.cnblogs.com/Wxtrkbc/p/5459941.html
Python之socketserver源码分析相关推荐
- 【python】Dpark源码分析
关于Dpark的PDF: http://velocity.oreilly.com.cn/2011/ppts/dpark.pdf 源码分析: Dpark/Spark中最重要的核心就是RDD(弹性分布式数 ...
- python成长之路10——socketserver源码分析
1 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 2 3 参数一:地址簇 4 socket.AF_INET ipv4(默认) 5 soc ...
- Python Requests库源码分析
1. Requests库简介 书籍是人类进步的阶梯,源码是程序员进步的阶梯.为了进步,我们就要不断地阅读源码,提升自己的技术水平.今天我们来剖析一下Python的Requests库. Requests ...
- python3 socketserver源码解析_解读python中SocketServer源码
再看继承 真正的大餐来之前,还是来点儿开胃菜!回顾一下关于类的继承的知识: 我们先看上面的代码,这是一个简单的类继承,我们可以看到父类Base和子类Son,它们中各有一个Testfunc方法,当我们实 ...
- GCN的Python实现与源码分析
目录 一.GCN基本介绍 二.GCN的Keras实现 Cora 数据集预处理 1. 将Cora节点的类别标签进行one-hot编码 2. 将Cora.cite文件转化为邻接矩阵形式 3. 将数据集划分 ...
- Python字典部分源码分析,字典是无序的
1 def clear(self): # real signature unknown; restored from __doc__ 2 """ D.clear() -& ...
- Python微型Web框架Bottle源码分析
Bottle 是一个快速,简单和轻量级的 WSGI 微型 Web 框架的 Python.它作为单个文件模块分发,除了 Python 标准库之外没有依赖关系. 选择源码分析的版本是 Release 于 ...
- zg手册 之 python2.7.7源码分析(1)-- python中的对象
为什么80%的码农都做不了架构师?>>> 源代码主要目录结构 Demo: python 的示例程序 Doc: 文档 Grammar: 用BNF的语法定义了Python的全部语法 ...
- python树状节点 可拖拽_Python 的 heapq 模块源码分析
原文链接:Python 的 heapq 模块源码分析 起步 heapq 模块实现了适用于Python列表的最小堆排序算法. 堆是一个树状的数据结构,其中的子节点都与父母排序顺序关系.因为堆排序中的树是 ...
最新文章
- C#面向对象(四)虚方法实现多态
- Python3 多线程问题:ModuleNotFoundError: No module named 'thread',原因及解决办法。
- logistic回归 简介_金融专业进!逻辑回归模型简述
- Android开发 ---如何操作资源目录中的资源文件2
- linux 命令启动菜单栏,Linux守护进程的启动方法
- 【转载】Debian 6安装小记
- 全国计算机等级考试贵州大学,贵州计算机等级考试报名入口
- 漫画:这份程序员自画像,是不是你的?
- 彻底搞懂Bert模型
- ReactJS基础(续)
- 粒子群优化算法python
- 网络安全web渗透技术
- java邮件服务器搭建_邮件服务器+javamail(仅包括发送)搭建
- 一分钟搞定网页监控,实现网站链接百度自动推送
- Django笔记09:一招解决使用regroup模板标签出现的重复分组问题
- oracle的shell命令,shell脚本执行oracle insert语句seraph
- 如何通便清肠快速见效_如何排毒清肠通便
- 5月全球CTF比赛时间汇总来了!
- 大地水准面 地球椭球体 大地基准面 地图投影理解
- DSX2-8000如何校准?校准流程?
热门文章
- android仿饿了么筛选,Android仿饿了么搜索功能
- 将两个文件的路径作为参数传递给脚本_将Docker与pipeline一起使用
- 华为hbase二级索引(secondary index)细节分析
- 进行有效客户细分的八个步骤
- 文字识别(五)--自然场景文本检测技术综述(CTPN, SegLink, EAST)
- 评分卡模型开发(七)--基于逻辑回归的标准评分卡实现
- opencv4 图像特征匹配_openCV - 图像特征匹配
- 简述中断的整个过程,谈谈你对中断在计算机中作用的理解。
- java 应用监控系统_GitHub - jiangbin216/JavaMonitor: 一款适用于Java应用的在线性能监控系统(JM)...
- c语言格式化浮点数多余的0