官方提供了socketserver包去方便咱们快速的搭建一个服务器框架。算法
server类
缓存
socketserver包提供5个Server类,这些单独使用这些Server类都只能完成同步的操做,他是一个单线程的,不能同时处理各个客户端的请求,只能按照顺序依次处理。安全
1
2
3
4
5
6
7
8
9
10
11
12
13
|
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
|
两个Mixin类
bash
1
2
3
|
+--------------+ +----------------+
| ForkingMixIn | | ThreadingMixIn |
+--------------+ +----------------+
|
各自实现了多进程和多线程的功能(ForkingMixIn在Windows不支持)服务器
因而将这些同步类和Mixin类组合就实现了异步服务类的效果。多线程
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
框架class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
异步
基本使用
socket
因为server须要同时处理来自多个客户端的请求,须要提供异步的支持,因此一般使用上面的异步类建立服务器。在Windows系统中没有提供os.fork()接口,Windows没法使用多进程的ForkingUDPServer和ForkingTCPServer,只能使用ThreadingTCPServer或者ThreadingUDPServer;而Linux和Unix多线程和多进程版本均可以使用。tcp
服务器主要负责接受客户端的链接请求,当一个新的客户端请求到来后,将分配一个新的线程去处理这个请求(异步服务器ThreadingTCPServer),而与客户端信息的交互则交给了专门的请求处理类(RequestHandlerClass)处理。
1
2
3
4
|
import
socketserver
# 建立一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息
server
=
socketserver.ThreadingTCPServer((
"127.0.0.1"
,
8000
), BaseRequestHandler)
server.serve_forever()
# 启动服务器,
|
只须要上面两行代码就能够建立开启一个服务,运行上面代码后常看本机8000端口,发现有程序正在监听。
C:\Users\user>netstat -anp tcp | findstr 8000
TCP 127.0.0.1:8000 0.0.0.0:0 LISTENING
ThreadingTCPServer能够对咱们的请求进行接受,可是并不会进行处理请求,处理请求的类是上面指定BaseRequestHandler类,该类能够定义handle方法来处理接受的请求。
BaseRequestHandler的源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class
BaseRequestHandler:
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()
def
setup(
self
):
pass
def
handle(
self
):
pass
def
finish(
self
):
pass
|
在server = socketserver.ThreadingTCPServer(("127.0.0.1", 8000), BaseRequestHandler)中,BaseRequestHandler将做为参数绑定到服务器的实例上,服务器启动后,每当有一个新的客户端接接入服务器,将会实例化一个请求处理对象,并传入三个参数,request(链接客户端的socket)、client_address(远程客户端的地址)、server(服务器对象),执行init方法,将这三个参数保存到对应属性上。这个请求处理对象即可以与客户端交互了。
简单示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import
socketserver
import
threading
class
MyRequestHandler(socketserver.BaseRequestHandler):
""" BaseRequestHandler的实例化方法中,得到了三个属性
self.request = request # 该线程中与客户端交互的 socket 对象。
self.client_address # 该线程处理的客户端地址
self.server = server # 服务器对象
"""
def
handle(
self
):
while
True
:
msg
=
self
.request.recv()
# 接受客户端的数据
if
msg
=
=
b
"quit"
or
msg
=
=
"":
# 退出
break
print
(msg.decode())
self
.request.send(msg)
# 将消息发送回客户端
def
finish(
self
):
self
.request.close()
# 关闭套接字
if
__name__
=
=
"__main__"
:
# 建立一个基于TCP的server对象,并使用BaseRequestHandler处理客户端发送的消息
server
=
socketserver.ThreadingTCPServer((
"127.0.0.1"
,
8000
), MyRequestHandler)
server.serve_forever()
# 启动服务器
|
咱们建立了一个ThreadingTCPServer服务器,而后在传入的处理类MyRequestHandler,并在handle方法中提供与客户端消息交互的业务逻辑,此处只是将客户端的消息返回客户端。最后咱们在finish方法中关闭资源,finish方法使用了finally机制,保证了这些代码必定会执行。
上一篇使用socket实现了一个群聊服务器,这个里使用socketServer将更加方便的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class
MyRequestHandle(BaseRequestHandler):
clients
=
{}
# 在类属性中记录全部与客户端链接socket。
lock
=
threading.Lock()
# 互斥锁,各个线程共用
def
setup(
self
):
# 新的用户链接时,预处理,将这个新的链接加入到clients中,考虑线程安全,须要加锁
with
self
.lock:
self
.clients[
self
.client_address]
=
self
.request
def
handle(
self
):
# 处理客户端的请求主逻辑
while
True
:
data
=
self
.request.recv(
1024
).strip()
# 接受数据
if
data
=
=
b
"quit"
or
data
=
=
b"":
# 客户端退出
with
self
.lock:
self
.server.clients.pop(
self
.client_address)
self
.request.close()
break
print
(
"{}-{}: {}"
.
format
(
*
self
.client_address, data.decode()))
with
self
.lock:
for
_, c
in
self
.server.clients.items():
# 群发
c.send(data)
def
finish(
self
):
with server.lock:
for
_, c
in
server.clients.items():
c.close()
server.server_close()
def
main():
server
=
ThreadingTCPServer((
"127.0.0.1"
,
8000
), MyRequestHandle)
# 将建立的全部线程设置为daemon线程,这样控台主程序退出时,这个服务器的全部线程将会被结束
server.daemon_threads
=
True
if
__name__
=
=
"__main__"
:
main()
|
上面requestHandlerclass中的handle方法和finish方式对应了上一篇中TCP服务器的recv方法和stop方法,他们处理请求的逻辑是相同的。只是上面使用了socketserver的代码变少了,处理的逻辑也变少了,TCPserver帮咱们完成了大量的工做,这利于软件的快速开发。
内置的两个RequestHandlerClass
StreamHandlerRequest
StreamHandlerRequest顾名思义是一种流式的求情处理类,对应TCP协议的面向字节流的传输形式。咱们从源代码分析。(去除了一些次要代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
class
StreamRequestHandler(BaseRequestHandler):
rbufsize
=
-
1
# 读缓存
wbufsize
=
0
# 写缓存
timeout
=
None
# 超时时间
# IP/TCP拥塞控制的Nagle算法算法。
disable_nagle_algorithm
=
False
def
setup(
self
):
# 实现了setup,
self
.connection
=
self
.request
if
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
)
# 使用 makefile方法得到了一个只读文件对象 rfile
self
.rfile
=
self
.connection.makefile(
'rb'
,
self
.rbufsize)
# 得到一个只写的文件对象 wfile
if
self
.wbufsize
=
=
0
:
self
.wfile
=
_SocketWriter(
self
.connection)
else
:
self
.wfile
=
self
.connection.makefile(
'wb'
,
self
.wbufsize)
def
finish(
self
):
# 负责将这个 wfile 和 rfile方法关闭。
if
not
self
.wfile.closed:
try
:
self
.wfile.flush()
except
socket.error:
pass
self
.wfile.close()
self
.rfile.close()
|
使用StreamRequestHandler方法能够将这个socket包装成一个类文件对象,方便咱们使用一套文件对象的方法处理这个socket,它没有实现handle方法,我仍然须要咱们实现。咱们能够这样使用它
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class
MyHandle(StreamRequestHandler):
# 若是须要使用setup和finish方法,须要调用父类方法,不然该方法将会被覆盖。
def
setup(
self
):
super
().setup()
# 添加本身的需求
def
handle(
self
):
# 这里咱们可使用wfile和rfile来处理socket消息了,例如以前使用self.request.recv()方法等同于self.rfile.read()
# 而 self.wfile.write 等同于 self.request.send(),在handle方法中完成业务逻辑便可
def
finish(
self
):
super
().finish()
server
=
ThreadingTCPServer(
"127.0.0.1"
, MyHandle)
server.serve_forever()
|
StreamRequestHandler主要定义了两个新的 wfile对象和rfile对象,来分别对这个socket进行读写操做,当咱们业务须要时,好比须要使用文件接口方法时,选择继承于StreamRequestHandler构建咱们本身处理请求类来完成业务逻辑将会更加的方便。
DatagramRequestHandler
DatagramRequestHandler字面意思是数据报请求处理,也就是基于UDPServer的服务器才能使用该请求处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
DatagramRequestHandler(BaseRequestHandler):
def
setup(
self
):
from
io
import
BytesIO
# udp的self.request包含两部分(data,socket)它来自于
# data, client_addr = self.socket.recvfrom(self.max_packet_size)
# return (data, self.socket), client_addr
# (data, self.socket)就是这个self.request,在这里将其解构,data为recvfrom接收的数据
self
.packet,
self
.socket
=
self
.request
# 该数据包封装为 BytesIO,一样为一个类文件对象。
self
.rfile
=
BytesIO(
self
.packet)
self
.wfile
=
BytesIO()
def
finish(
self
):
self
.socket.sendto(
self
.wfile.getvalue(),
self
.client_address)
|
从源码能够看出,DatagramRequestHandler将数据包封装为一个rfile,并实例化一个ByteIO对象用于写入数据,写入的数据能够经过self.socket这个套接字发送。这样可使用rfile和wfile这两个类文件对象的read或者write接口来进行一些IO方面的操做。
本文出自https://www.jb51.net/article/188551.htm