python --- Socketserver N部曲(1)
曲一
socketserver
是为了简化服务器端开发而产生的,是一个高级的标准库。(背景介绍完毕,开始干)
一些概念
来自源码的一张图片,简洁又FengSao
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
这是socketserver
的一个基本框架,BaseServer
是基类,是不可以初始化的。然后我们可以利用BaseServer
的子类就是下面的四个,来实例化我们需要的服务器类型。(本人太菜只知道一点TCP的相关知识,所以从TCPserver
入手学习)。
看到这个图开始还是很开心的,以为就这图上的5个类,结果大神写的源码把我一个英语不好的“澜”差点看哭了。我开始发现没那么简单,有好多的类,好多好多的类!
一行一行的看绝对懵逼,所以我决定不看其他的内容只看关于TCPserver
的(好机智,好吧其实还是一脸懵逼),如果你也和我一样看到源码不知从何下手,下面给出一个我的查看路线吧(一行一行爬出来的一条路,当然只是TCPserver
相关的内容)。
我用的pycharm
,鼠标放在想查看源码的对象上,ctrl + b
就可以查看源码,还是非常方便的,其他的编辑器我就不知道了,下面开始。
不要一行一行的看了(想想当初憨憨的我...)先搜关键词BaseRequestHandler
,你会找到一个独立的类,看下面的说明有这样一段话
This class is instantiated for each request to be handled. The constructor sets the instance variables request, client_address and server, and then calls the handle() method. To implement a specific service, all you need to do is to derive a class which defines a handle() method.
The handle() method can find the request as self.request, the client address as self.client_address, and the server (in case it needs access to per-server information) as self.server. Since a separate instance is created for each request, the handle() method can define other arbitrary instance variables.
注意加粗的句子,貌似告诉我们只需要派生一个BaseRequestHandler
的子类,并且重写一下handle()
方法就可以了,怎么样很简单的样子有没有。好写一个这个类的子类先上代码
# Bianry_socketserver_v1
import socketserver
class my_tcphandler(socketserver.BaseRequestHandler):
def handle(self):
pass # 不知道这个函数干嘛的的,先占个坑
继续看源码(心情沉重没有办法),继续搜关键字BaseRequestHandler
,让我们把视线转到源码里的第一个关键词位置
To implement a service, you must derive a class from BaseRequestHandler and redefine its handle() method. You can then run various versions of the service by combining one of the server classes with your request handler class.
看到还是让我们重写一个BaseRequestHandler
的handle()
方法,然后注意有一个关键词server classes
,嗯!我们不是要一个TCPserver
吗?貌似看到希望了啊。提示告诉我们要把自己写的request handler class
和server class
结合一下,怎么结合啊,别慌,关键字搜索TCPServer
目光再次转移...
直接看TCPServer
的构造函数(为什么?因为我在它的参数里看到了BaseRequestHandlerClass
)
# constractor of TCPServer
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
"""Constructor. May be extended, do not override.""" # 别人的函数叫你不要随便该
BaseServer.__init__(self, server_address, RequestHandlerClass)
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
看到他又调用了父类的构造函数,而且把我们想要的东西传了进去,那就查看它父类的构造函数,走起...
# BaseServer constractor
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
看到父类就把它存了起来...就没了吗?不行继续搜RequestHandlerClass
找到一个finish_request
函数
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
终于看到一点实际的了,看到这个函数把我们传入的我们自己写的类my_tcphandler
(继承的BaseRequestHandler
)实例化了,好激动赶紧回去看看BaseRequestHandler
构造函数有什么,因为自己写的my_tcphandler
类没有构造函数,所以实例化会执行父类的构造函数先。
# BaseRequestHandler constractor
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
看到它加了几个变量进去request
,clinet_address
,server
,而且先调用setup()
函数,然后再handle()
'(还占着一个坑的函数,等下去填上),最后你呢回去执行finish()
,这三个函数都是没有写任何内容的,是给我们发挥的呀,刚刚只写了一个handle()
,那怎么行,待会儿把这两个也加进去。现在的问题是request
,clinet_address
,server
这三个变量是用来干嘛的呀,我们的三个函数怎么重构啊,我们要的是一个TCPServer
啊。
上面的问题先不管了(船到桥头自然直,安慰一下自己),下面来尝试搭建我们的第一个TCPServer
,看能不能行,先缕一缕思路
首先,我们要写一个BaseRequestHandler
的子类并重写handle()
方法,其他两个函数不是必须写的先不管。
然后,我们要实例化一个TCPServer
的对象,并且把我们写的类作为参数传进去。(而实例化的过程非常的坎坷就是我们上面一步一步分析得到那样子。)
最后...没了耶,就两部这么简单
好开始写
# Bianry_socketserver_v1
import socketserver
class my_tcphandler(socketserver.BaseRequestHandler):
def handle(self):
print('request:',self.request)
print('clinet_address:',self.client_address)
print('server:',self.server)
# 不是不晓得这是三个什么东西吗,打印它,不解释
if __name__ == '__main__':
addr,port = 'localhost',9999
tcp_server = socketserver.TCPServer((addr,port),my_tcphandler)
tcp_server.serve_forever() # keep running...
运行程序发现没反应,没打印我们想要内容,这东西就像个憨憨一样,憨憨的在等一个人的到来。于是我们找了一个人(客户端)来尝试去告诉他一些真相(连接TCPserver
),客户端程序
import socket
clinet = socket.socket()
clinet.connect(('localhost',9999))
clinet.close()
只是连接一下,就断开了,就是个过客...
回首看看服务器端,打印了我们想要的东西
request: <socket.socket fd=424, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 61933)>
clinet_address: ('127.0.0.1', 61933)
server: <socketserver.TCPServer object at 0x000001DE0F2DF5C0>
看到request
原来是一个套接字,clinet_address
原来是客户端留下的ip
和port
,srever
原来就是自己(服务端)啊。
现在貌似知道handle()
里应该写什么内容了,应该是和客户端交互的内容,因为我们有一个套接字(request
),有了套接字就可以和和客户端交互了。而且handle()
方法在客户端建立应该连接时才会执行,是不是想到了accept()
... ,个人理解就是差不多的(当然对单线程服务器,多线程的原理还不懂,菜哭...),源码上是这么说的
Since a separate instance is created for each request, the handle() method can define other arbitrary instance variables.
是差不多吧,不多废话了,上代码来。
# Bianry_socketTCPserver_v1
import socketserver
from time import ctime
class my_tcphandler(socketserver.BaseRequestHandler):
def handle(self):
try:
while True:
clinet_data = self.request.recv(1024)
if not clinet_data:
break
print('clinet:',clinet_data.decode())
self.request.sendall(b'[%s] %s' % (clinet_data,ctime().encode('utf-8')))
except Exception as e:
exit(e)
finally:
self.request.close()
if __name__ == '__main__':
addr,port = 'localhost',9999
tcp_server = socketserver.TCPServer((addr,port),my_tcphandler) # TCPServer
tcp_server.serve_forever() # keep running...
客户端
# Bianry_socketTCPclinet_v1
import socket
clinet = socket.socket()
clinet.connect(('localhost',9999))
while True:
data = input()
if not data:
break
clinet.send(data.encode('utf-8'))
server_data = clinet.recv(1024)
print('server:',server_data.decode())
clinet.close()
这是单线程的,不能同时处理多个连接请求,想要处理多个怎么办,加几个字母就可以了
# Bianry_socketThreadingTCPserver_v1
import socketserver
from time import ctime
class my_tcphandler(socketserver.BaseRequestHandler):
def handle(self):
try:
while True:
clinet_data = self.request.recv(1024)
if not clinet_data:
break
print('clinet:',clinet_data.decode())
self.request.sendall(b'[%s] %s' % (clinet_data,ctime().encode('utf-8')))
except Exception as e:
exit(e)
finally:
self.request.close()
if __name__ == '__main__':
addr,port = 'localhost',9999
tcp_server = socketserver.ThreadingTCPServer((addr,port),my_tcphandler) #ThreadingTCPServer
tcp_server.serve_forever() # keep running...
多线程还不会...菜鸡去学习了...
python --- Socketserver N部曲(1)的更多相关文章
- docker-compose下的java应用启动顺序两部曲之二:实战
上篇回顾 本文是<docker-compose下的java应用启动顺序两部曲>的终篇,在上一篇<docker-compose下的java应用启动顺序两部曲之一:问题分析>中,我 ...
- c语言项目开发流程二部曲
一.在第一部曲中我们介绍了电子词典项目开发的前5步,下面继续我们的步伐. 6.函数接口设计,这一步不是一蹴而就的,在项目进行中得不断修改,下面是我电子词典项目接口. /**************函数 ...
- JDBC编程六部曲
今天初学jdbc,明白了大致的编程流程,在此总结一下: JDBC编程可以分为六步——六部曲: * 第一步:注册驱动. * 1.1 获取驱动对象 * 1.2 注册驱动 * 第二步:获取数据库连接 * 第 ...
- 使用Python SocketServer快速实现多线程网络服务器
Python SocketServer使用介绍 1.简介: SocketServer是python的一个网络服务器框架,可以减少开发人员编写网络服务器程序的工作量. SocketServer总共有4个 ...
- docker-compose下的java应用启动顺序两部曲之一:问题分析
在docker-compose编排多个容器时,需要按实际情况控制各容器的启动顺序,本文是<docker-compose下的java应用启动顺序两部曲>的第一篇,文中会分析启动顺序的重要性, ...
- python socketserver实现客户端多并发
直接看代码 server #!/usr/bin/env python # -*- coding:utf-8 -*- import socketserver import subprocess clas ...
- python socketserver框架解析
socketserver框架是一个基本的socket服务器端框架, 使用了threading来处理多个客户端的连接, 使用seletor模块来处理高并发访问, 是值得一看的python 标准库的源码之 ...
- python - socketserver 模块应用
server端: import socketserver import subprocess import json import struct class MyTCPHandler(socketse ...
- Python SocketServer源码分析
1 XXXServer 1.1 BaseSever 提供基础的循环等待请求的处理框架.使用serve_forever启动服务,使用shutdown停止.同时提供了一些可自行扩展的方 ...
随机推荐
- Spring Cloud Sleuth + Zipkin 链路监控
原文:https://blog.csdn.net/hubo_88/article/details/80878632 在微服务系统中,随着业务的发展,系统会变得越来越大,那么各个服务之间的调用关系也就变 ...
- 接口自动化--数据驱动(ddt)
上次我们提到了unittest单元测试框架,运用单元测试框架unittest进行编写测试用例 但是遇到了一个问题,就是难道我一个测试点中有多个测试用例,我要每一个都要去编写一条测试用例嘛?这实在是太复 ...
- 使用Supervisor管理Django应用进程
官方文档 1.安装 pip install supervisor 2.使用说明 2.1 查看默认配置 echo_supervisord_conf 一般情况下,不需要去修改默认配置,而是将默认配置重定 ...
- docker的简单操作和端口映射
一:简介 Docker镜像 在Docker中容器是基于镜像启动的 镜像是启动容器的核心 镜像采用分层设计,最顶层为读写层 使用快照COW技术,确保底层不丢失 通过ifconfig(ip a)来查看d ...
- WebForm 打开默认页
原文:https://www.cnblogs.com/lionden/p/3728716.html <configuration> <system.webServer> < ...
- [BZOJ1015/JSOI2008]星球大战
// 此博文为迁移而来,写于2015年7月16日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w6le.html 1.题 ...
- 网络协议 13 - HTTPS 协议
之前说了 HTTP 协议的各种问题,但是它还是陪伴着互联网.陪伴着我们走过了将近二十年的风风雨雨.现在有很多新的协议尝试去取代它,来解决性能.效率等问题,但它还还能靠着“多年的情分”活的滋润.然而,近 ...
- Java 集合系列之六:工具类Collections和Arrays基本操作
1. Collections Collections类主要是完成了两个主要功能 提供了若干简单而又有用的算法,比如排序,二分查找,求最大最小值等等. 提供对集合进行包装的静态方法.比如把指定的集合包装 ...
- Mac-搭建Hadoop集群
You have to work very hard to believe that you are really powerless. Mac-搭建Hadoop集群 我用到了:VMware Fusi ...
- 记C# 调用虹软人脸识别 那些坑
上一个东家是从事安防行业的,致力于人工智能领域,有自主人脸识别.步态识别的算法.C++同事比较称职有什么问题都可以第一时间反馈,并得到合理的处理,封装的DLL 是基于更高性能的GPU算法,可支持更多线 ...