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停止.同时提供了一些可自行扩展的方 ...
随机推荐
- Fluter基础巩固之Dart语言详解<三>
继续Dart语言的学习,这次过后下次就进入全新的Flutter的学习了,小小的激动.. 操作符重载: C++中也有,咱们来看一下在Dart中是如何来实现的: 比较简单. 异步[重要!]: async和 ...
- vue - 过滤器-钩子函数路由
一.关于路由 1.使用vue router 本质上是声明一种可以通过路径进行 挂子,用子 找到对应的 template 进行页面渲染 <!DOCTYPE html> <html la ...
- css 高度随宽度比例变化
[方案一:padding实现] 原理: 一个元素的 padding,如果值是一个百分比,那这个百分比是相对于其父元素的宽度而言的,padding-bottom 也是如此. 使用 padding-bot ...
- LOJ 3119: 洛谷 P5400: 「CTS2019 | CTSC2019」随机立方体
题目传送门:LOJ #3119. 题意简述: 题目说的很清楚了. 题解: 记恰好有 \(i\) 个极大的数的方案数为 \(\mathrm{cnt}[i]\),则答案为 \(\displaystyle\ ...
- Discuz!开发之时间处理函数dgmdate()详解
使用过Discuz!的朋友都会知道Discuz!的时间可以显示成多少秒前.多少分钟前.几个小时前.几天前等等,而不是单纯的显示标准时间,这样的时间显示方式就更显得人性化了! 那么Discuz!是如 ...
- wordpress怎么用AMP加速器呢
AMP项目(Accelerated Mobile Pages)是一个开放源代码计划,旨在为所有人打造更好的网络体验.借助该项目,用户可以打造出在各种设备和分发平台上都能始终如一地快速加载且效果出色的精 ...
- 常用dos命令(3)
网络命令 ping 进行网络连接测试.名称解析 ftp 文件传输 net 网络命令集及用户管理 telnet 远程登陆 ipconfig显示.修改TCP/IP设置 msg 给用户发送消息 arp 显示 ...
- [KCOJ20170214]又一个背包
题目描述 Description 小W要去军训了!由于军训基地是封闭的,小W在军训期间将无法离开军训基地.所以他没有办法出去买他最爱吃的零食.万般无奈的小W只好事先买好他爱吃的零食,装在背包里带入军训 ...
- django -- ORM查询
前戏 在我们之前操作ORM中,你也许是启动Django项目,通过地址访问固定的函数,或者在pycharm里的python console里执行,第一种比较麻烦,而且每次都要启动项目,写路由,第二种虽然 ...
- CF888G XOR-MST trie,贪心
CF888G XOR-MST 链接 CF888G 思路 trie上贪心,先左右两边连边,再用一条边的代价连起左右两颗树.因为内部的边一定比跨两棵树的边权笑,显然是对的. 代码自己瞎yy的.启发式合并 ...