Python3学习之路~8.5 SocketServer实现多并发
前面几节我们写的socket都只能实现服务端与一个客户端通信,并不能实现服务端与多客户端同时通信。接下来我们就来学习一下如何实现服务端同时与多个客户端通信,即并发。
Socket Server
socketserver就是对socket的一个再封装,主要功能就是实现并发。
socketserver模块简化了编写网络服务器的任务。
socketserver一共有以下4种类型:
class socketserver.TCPServer(server_address,RequestHandlerClass,bind_and_activate = True)
它使用Internet TCP协议,该协议在客户端和服务器之间提供连续的数据流。
class socketserver.UDPServer(server_address,RequestHandlerClass,bind_and_activate = True)
它使用数据报,这些数据报是可能无序到达或在传输过程中丢失的离散信息包。参数与TCPServer相同。
class socketserver.UnixStreamServer(server_address,RequestHandlerClass,bind_and_activate = True)
class socketserver.UnixDatagramServer(server_address,RequestHandlerClass,bind_and_activate = True)
这些是不经常使用的 类似于TCP和UDP类 的类,但使用Unix域套接字。它们不适用于非Unix平台,参数与TCPServer相同。
如下继承图中有五个类,并且分别显示了他们的继承关系,其中四个代表四种类型的同步服务器:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
注意:它们都继承了同一个基类:BaseServer。
创建一个 socketserver 至少分以下几步:
1.自己创建一个请求处理类,并且这个类要继承BaseRequestHandler类,并且还要重写父亲类里的handle()方法,此方法将用来处理传入的请求,即跟客户端所有的交互都是在handle()里完成的。
2.实例化一个SocketServer类(4种类型选其1,比如TCPServer),并且传递server address和 你上面创建的请求处理类 给这个SocketServer。
3.调用SocketServer对象的handle_request()或者serve_forever()方法来处理一个或多个请求。
server.handle_request() # 只能处理一个请求,因为处理完一个就退出了,一般不用它
server.serve_forever() # 可处理多个请求,因为永远执行
4.调用server_close()来关闭套接字。
实例1:基本的socketserver代码
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server. It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
""" def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# just send back the same data, but upper-cased
self.request.send(self.data.upper()) if __name__ == "__main__":
HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
socketserver1.py
上面是一个最简单的socketserver代码,只能处理一个请求,比如运行如下客户端代码,通过结果你就会发现,处理第二个请求就会失败。
#Author:Zheng Na # 客户端 import socket client = socket.socket()
client.connect(('localhost',9999)) while True:
msg = input(">>: ").strip()
client.send(msg.encode("UTF-8")) data = client.recv(1024) # 接收1024字节
print("recv from server: ",data) client.close()
socketclient.py 客户端代码
D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: aa
recv from server: b'AA'
>>: bb
Traceback (most recent call last):
File "D:/python-study/s14/Day08/socketclient.py", line 15, in <module>
data = client.recv(1024) # 接收1024字节
ConnectionAbortedError: [WinError 10053] 您的主机中的软件中止了一个已建立的连接。 Process finished with exit code 1
运行结果
实例2:处理多个请求
如果想要它处理多个请求,那么就要自己在handle()方法中加循环,跟前面我们学的socket实例一样。
#Author:Zheng Na import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self):
while True:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# 下面3行代码防止服务器端随着客户端的断开而断开
# 经过测试发现,在linux有效,Windows无效。
if not self.data:
print(self.client_address,"断开了")
break
self.request.send(self.data.upper()) if __name__ == "__main__":
HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
socketserver2.py 可处理多个请求
上段代码你在linux运行是没问题的,但是在Windows上面运行的话,一旦你关闭了客户端,服务端就会报错ConnectionResetError
D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: aa
recv from server: b'AA'
>>: bb
recv from server: b'BB'
>>:
Process finished with exit code -1 D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketserver2.py
127.0.0.1 wrote:
b'aa'
127.0.0.1 wrote:
b'bb'
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 53532)
Traceback (most recent call last):
File "D:\software\Python3.6.5\lib\socketserver.py", line 317, in _handle_request_noblock
self.process_request(request, client_address)
File "D:\software\Python3.6.5\lib\socketserver.py", line 348, in process_request
self.finish_request(request, client_address)
File "D:\software\Python3.6.5\lib\socketserver.py", line 361, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "D:\software\Python3.6.5\lib\socketserver.py", line 696, in __init__
self.handle()
File "D:/python-study/s14/Day08/socketserver2.py", line 9, in handle
self.data = self.request.recv(1024).strip()
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
Windows运行结果
实例3:
如果想要在Windows上运行不报错,我们可以尝试主动抓住这个错误。
#Author:Zheng Na import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self):
while True:
try:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# 下面3行代码防止服务器端随着客户端的断开而断开
# 经过测试发现,在linux有效,Windows无效。
if not self.data:
print(self.client_address, "断开了")
break
self.request.send(self.data.upper())
except ConnectionResetError as e:
print('出现错误',e)
break if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
socketserver3.py linux & Windows运行正常
D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: aa
recv from server: b'AA'
>>: bb
recv from server: b'BB'
>>:
Process finished with exit code -1 D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketserver3.py
127.0.0.1 wrote:
b'aa'
127.0.0.1 wrote:
b'bb'
出现错误 [WinError 10054] 远程主机强迫关闭了一个现有的连接。
Windows运行结果
此时,你就可以实现关闭一个客户端后,继续重新打开另一个客户端向服务器发送数据。
实例4:ThreadingTCPServer 实现并发
但你发现,上面的代码,依然不能同时处理多个连接,哎?那我搞这个干嘛?别急,不是不能处理多并发,如果你想,你还要启用多线程,多线程我们现在还没学,但你大体知道,有了多线程,就能同时让cpu干多件事了。
让你的socketserver并发起来, 必须选择使用以下一个多并发的类
class socketserver.ForkingTCPServer # 多进程 class socketserver.ForkingUDPServer class socketserver.ThreadingTCPServer # 多线程 class socketserver.ThreadingUDPServer
so 只需要把下面这句
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
换成下面这个,就可以多并发了,这样,客户端每连进一个来,服务器端就会分配一个新的线程来处理这个客户端的请求
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
运行代码
#Author:Zheng Na import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self):
while True:
try:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# 下面3行代码防止服务器端随着客户端的断开而断开
# 经过测试发现,在linux有效,Windows无效。
if not self.data:
print(self.client_address, "断开了")
break
self.request.send(self.data.upper())
except ConnectionResetError as e:
print('出现错误',e)
break if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
socketserver4_thread_concurrence.py
输出结果
D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketserver4_thread_concurrence.py
127.0.0.1 wrote:
b'from client1'
127.0.0.1 wrote:
b'from client2'
127.0.0.1 wrote:
b'from client3' D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: from client1
recv from server: b'FROM CLIENT1'
>>: D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: from client2
recv from server: b'FROM CLIENT2'
>>: D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketclient.py
>>: from client3
recv from server: b'FROM CLIENT3'
>>:
windows同时运行3个客户端
[root@hadoop my-test-files]# python3 socketserver4_thread_concurrence.py
127.0.0.1 wrote:
b'from client1'
127.0.0.1 wrote:
b'from client2'
127.0.0.1 wrote:
b'from client3' [root@hadoop my-test-files]# python3 socketclient.py
>>: from client1
recv from server: b'FROM CLIENT1'
>>: [root@hadoop my-test-files]# python3 socketclient.py
>>: from client2
recv from server: b'FROM CLIENT2'
>>: [root@hadoop my-test-files]# python3 socketclient.py
>>: from client3
recv from server: b'FROM CLIENT3'
>>:
linux同时运行3个客户端
实例5:ForkingTCPServer 实现并发
同实例4类似,只是这次将下面这句
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
换成了下面这个
server = socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler)
运行代码
#Author:Zheng Na import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): def handle(self):
while True:
try:
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# 下面3行代码防止服务器端随着客户端的断开而断开
# 经过测试发现,在linux有效,Windows无效。
if not self.data:
print(self.client_address, "断开了")
break
self.request.send(self.data.upper())
except ConnectionResetError as e:
print('出现错误',e)
break if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
socketserver5_fork_concurrence.py
输出结果
# 我的运行结果(Windows Python3.6.5):一运行就报错
D:\software\Python3.6.5\python.exe D:/python-study/s14/Day08/socketserver5_fork_concurrence.py
Traceback (most recent call last):
File "D:/python-study/s14/Day08/socketserver5_fork_concurrence.py", line 27, in <module>
server = socketserver.ForkingTCPServer((HOST, PORT), MyTCPHandler)
AttributeError: module 'socketserver' has no attribute 'ForkingTCPServer' Process finished with exit code 1 老师的运行结果:启动3个客户端后才报错
AttributeError: module 'os' has no attribute 'fork' 总之一句话,ForkingTCPServer在Windows上不好使
Windows 运行报错
[root@hadoop my-test-files]# python3 socketserver5_fork_concurrence.py
127.0.0.1 wrote:
b'from client1'
127.0.0.1 wrote:
b'from client2'
127.0.0.1 wrote:
b'from client3' [root@hadoop my-test-files]# python3 socketclient.py
>>: from client1
recv from server: b'FROM CLIENT1'
>>: [root@hadoop my-test-files]# python3 socketclient.py
>>: from client2
recv from server: b'FROM CLIENT2'
>>: [root@hadoop my-test-files]# python3 socketclient.py
>>: from client3
recv from server: b'FROM CLIENT3'
>>:
linux同时运行3个客户端 成功
注意:ForkingTCPServer在Windows上不好使,在linux上百分比好使(效果与ThreadingTCPServer一模一样)。
总结:TCPServer VS ThreadingTCPServer VS ForkingTCPServer
TCPServer是接收到请求后执行handle方法,如果前一个的handle没有结束,那么其他的请求将不会受理,新的客户端也无法加入。
而ThreadingTCPServer和ForkingTCPServer则允许前一连接的handle未结束也可受理新的请求和连接新的客户端,区别在于前者用建立新线程的方法运行handle,后者用新进程的方法运行handle。
class socketserver.
BaseServer
(server_address, RequestHandlerClass) 主要有以下方法
class socketserver.BaseServer(server_address, RequestHandlerClass)
This is the superclass of all Server objects in the module. It defines the interface, given below, but does not implement most of the methods, which is done in subclasses. The two parameters are stored in the respective server_address and RequestHandlerClass attributes. fileno() # 返回文件描述符,这个一般是系统内部调用时用到的,我们一般用不到,知道即可
Return an integer file descriptor for the socket on which the server is listening. This function is most commonly passed to selectors, to allow monitoring multiple servers in the same process. handle_request() # 处理单个请求,我们一般也不用
Process a single request. This function calls the following methods in order: get_request(), verify_request(), and process_request(). If the user-provided handle() method of the handler class raises an exception, the server’s handle_error() method will be called. If no request is received within timeout seconds, handle_timeout() will be called and handle_request() will return. serve_forever(poll_interval=0.5) # 一直处理请求,直到收到一个明确的shutdown()请求。每0.5秒检查一下是否有程序给我发了shutdown的信号。
Handle requests until an explicit shutdown() request. Poll for shutdown every poll_interval seconds. Ignores the timeout attribute. It also calls service_actions(), which may be used by a subclass or mixin to provide actions specific to a given service. For example, the ForkingMixIn class uses service_actions() to clean up zombie child processes. Changed in version 3.3: Added service_actions call to the serve_forever method. service_actions() # Python3.3引入,被serve_forever()调用。
This is called in the serve_forever() loop. This method can be overridden by subclasses or mixin classes to perform actions specific to a given service, such as cleanup actions. New in version 3.3. shutdown() # 告诉serve_forever()停止处理请求
Tell the serve_forever() loop to stop and wait until it does. server_close() # 关闭
Clean up the server. May be overridden. address_family # 地址簇
The family of protocols to which the server’s socket belongs. Common examples are socket.AF_INET and socket.AF_UNIX. RequestHandlerClass # 请求处理类
The user-provided request handler class; an instance of this class is created for each request. server_address # 地址
The address on which the server is listening. The format of addresses varies depending on the protocol family; see the documentation for the socket module for details. For Internet protocols, this is a tuple containing a string giving the address, and an integer port number: ('127.0.0.1', 80), for example. socket # 套接字
The socket object on which the server will listen for incoming requests. The server classes support the following class variables: allow_reuse_address # 允许重用地址
Whether the server will allow the reuse of an address. This defaults to False, and can be set in subclasses to change the policy. request_queue_size # 暂时不用管它
The size of the request queue. If it takes a long time to process a single request, any requests that arrive while the server is busy are placed into a queue, up to request_queue_size requests. Once the queue is full, further requests from clients will get a “Connection denied” error. The default value is usually 5, but this can be overridden by subclasses. socket_type # 协议类型
The type of socket used by the server; socket.SOCK_STREAM and socket.SOCK_DGRAM are two common values. timeout # 超时时间,在handle_request()中使用,由于我们不同handle_request(),所以不用管它
Timeout duration, measured in seconds, or None if no timeout is desired. If handle_request() receives no incoming requests within the timeout period, the handle_timeout() method is called. There are various server methods that can be overridden by subclasses of base server classes like TCPServer; these methods aren’t useful to external users of the server object. finish_request()
Actually processes the request by instantiating RequestHandlerClass and calling its handle() method. get_request()
Must accept a request from the socket, and return a 2-tuple containing the new socket object to be used to communicate with the client, and the client’s address. handle_error(request, client_address)
This function is called if the handle() method of a RequestHandlerClass instance raises an exception. The default action is to print the traceback to standard output and continue handling further requests. handle_timeout()
This function is called when the timeout attribute has been set to a value other than None and the timeout period has passed with no requests being received. The default action for forking servers is to collect the status of any child processes that have exited, while in threading servers this method does nothing. process_request(request, client_address)
Calls finish_request() to create an instance of the RequestHandlerClass. If desired, this function can create a new process or thread to handle the request; the ForkingMixIn and ThreadingMixIn classes do this. server_activate()
Called by the server’s constructor to activate the server. The default behavior for a TCP server just invokes listen() on the server’s socket. May be overridden. server_bind()
Called by the server’s constructor to bind the socket to the desired address. May be overridden. verify_request(request, client_address)
Must return a Boolean value; if the value is True, the request will be processed, and if it’s False, the request will be denied. This function can be overridden to implement access controls for a server. The default implementation always returns True.
ThreadingTCPServer
Python3学习之路~8.5 SocketServer实现多并发的更多相关文章
- Python3学习之路~0 目录
目录 Python3学习之路~2.1 列表.元组操作 Python3学习之路~2.2 简单的购物车程序 Python3学习之路~2.3 字符串操作 Python3学习之路~2.4 字典操作 Pytho ...
- Python3学习之路~8.6 开发一个支持多用户在线的FTP程序-代码实现
作业: 开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...
- Python3学习之路~9.4 队列、生产者消费者模型
一 队列queue 当必须在多个线程之间安全地交换信息时,队列在线程编程中特别有用. 队列的作用:1.解耦,使程序直接实现松耦合 2.提高处理效率 列表与队列都是有顺序的,但是他们之间有一个很大的区别 ...
- Python3学习之路
python基础知识点 1.python基础知识点汇总 2.python常用数据类型 3.python之列表 4.python之字符串 5.python常用数据运算符 6.python之字典 7.py ...
- Python3学习之路~9.2 操作系统发展史介绍、进程与线程区别、线程语法、join、守护线程
一 操作系统发展史介绍 参考链接:http://www.cnblogs.com/alex3714/articles/5230609.html 二 进程与线程 进程: 对各种资源管理的集合 就可以称为进 ...
- Python3学习之路~9.1 paramiko模块:实现ssh执行命令以及传输文件
我们一般使用linux的时候,都是在Windows上安装一个ssh客户端连接上去.那么从一台linux如何连接到另一条linux呢?使用ssh命令即可,因为每台linux机器自己都有一个ssh客户端. ...
- Python3学习之路~8.4 利用socket实现文件传送+MD5校验
利用socket实现文件传送,大约分为如下几步: 1.读取文件名2.检测文件是否存在3.打开文件(别忘了最后关闭文件)4.检测文件大小5.发送文件大小给客户端6.等客户端确认7.开始边读边发数据8.m ...
- Python3学习之路~7.5 异常处理
1.异常基础 在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是现实一个提示的页面,通俗来说就是不让用户看见大黄页!!! try: pass except Excepti ...
- Python3学习之路~5.12 hashlib & hmac & md5 & sha模块
hashlib模块用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法 import md5 h ...
随机推荐
- gerrit和git
1.git Git是什么? Git是目前世界上最先进的分布式版本控制系统. SVN是集中式版本控制系统. Git与svn比较 相同:能记录文件的所有更改记录.这样是为了大量更改后,可以有记录回到过去, ...
- linux在telnet情况下root登录提示login incorrect
root登录时总是提示 login incorrect SSH可以登录 别的用户也可以通过telnet登录 唯独root不可以 解决方法: vi /etc/pam.d/login # auth r ...
- 一 期末架构1 centos7 简介
一 centos7 安装 和差异 虚拟机配置好硬件 第一张网卡NAT添加第二张网卡选择LAN区段 安装前修改内核 变更网卡名 net.ifnames=0 biosdevname=0 回车 ...
- [原创]基于Zynq AXI-GPIO Standalone & Linux 例程
基于Zynq AXI-GPIO Standalone & Linux 例程 待添加完善中
- Elasticsearch 简单快照备份
创建仓库 POST http://10.10.14.201:9200/_snapshot/backup { "type": "fs", "settin ...
- [转] vue&webpack多页面配置
前言 最近由于项目需求,选择使用vue框架,webpack打包直接使用的vue-cli,因为需要多页面而vue-cli只有单页面,所以就决定修改vue-cli的配置文件来满足开发需求. html-we ...
- tensorflow安装-【老鱼学tensorflow】
TensorFlow是谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理.Tensor(张量)意味着N维数组,Flow(流)意味着基于数据流图的计算,Tensor ...
- tensorflow结果可视化-【老鱼学tensorflow】
这次我们把上次的结果进行可视化显示,我们会把神经网络的优化过程以图像的方式展示出来,方便我们了解神经网络是如何进行优化的. 首先,我们把测试数据显示出来: # 显示测试数据 fig = plt.fig ...
- ssh报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
今天登陆远程主机的时候,出现如下的报错信息 ssh 10.0.0.1 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WAR ...
- add, subtract, multiply, divide
加.减.乘.除:add, subtract, multiply, divide