Python网络编程篇之socketserver

发表于:2017-11-28 09:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:gregory2017    来源:博客园

  1.socketserver模块和类
  socketserver是标准库中的一个高级模块,目标是简化很多样板代码(创建网络客户端和服务器所必须的代码)
  这个模块封装了socket编程所需要的各种各样的类,现在可以使用类来编写应用程序。
  因为以面向对象的方式处理事务有助于组织数据,以及逻辑性地将功能放在正确的地方,应用程序现在是时间驱动的,这意味着只有在系统中的时间发生时,它才会工作。
  SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进程” 专门负责处理当前客户端的所有请求。
  socketserver模块可以简化网络服务器的编写,Python把网络服务抽象成两个主要的类
  一个是Server类,用于处理连接相关的网络操作
  另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。
  类的描述
  1.BaseServer      包含核心服务器功能和 mix-in类的钩子;仅用于推导,这样不会创建这个类的实例;可以用TCPServer 或 UDPServer 创建类的实例
  2.TCPServer/UDPServer            基础的网络同步TCP/UDP服务器
  3.UnixStreamServer/UnixDatagramServer    基于文件的基础同步TCp/UDP服务器
  4.ForkingMixIn/ThreadingMixIn          核心派出或线程功能;只用做mix-in类与一个服务器类配合实现一些异步性;不能直接实例化这个类
  5.ThreadingTCPServer/ThreadingUDPServer  ThreadingMixIn 和 TCPServer/UDPServer的组合
  6.ForkingTCPServer/ForkingUDPServer     ForkingMixIn 和 TCPServer/UDPServer的组合
  7.BaseRequestHandler包含处理服务器请求的核心功能;仅仅用于推导,这样无法创建这个类的实例;可以用StreamRequestHandler 或 DatagramRequestHandler 创建类的实例
  8.StreamRequestHandler/DatagramRequestHandler实现Tcp/UDP服务器的服务处理器
  四个基础类的继承关系
+------------+
| BaseServer |
+------------+
|
v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
|
v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+
  这四个类使用"同步"来处理请求。只有处理完所有请求后,才可以开始处理新的请求!不适合使用在处理单个请求需要花费大量时间的场合。因为需要花费大量的计算时间,或者这因为它会返回大量的数据导致客户端处理速度变得很慢。解决方法是创建单独的进程或者线程处理每一个请求。在类内部的ForkingMixIn和ThreadingMixIn 组合可以支持"异步"的操作。
  所以,让你的socketserver并发起来, 必须选择使用以下一个多并发的类
  class socketserver.ForkingTCPServer
  class socketserver.ForkingUDPServer
  class socketserver.ThreadingTCPServer
  class socketserver.ThreadingUDPServer
  2.ThreadingTCPServer
  ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
  首先,必须通过继承BaseRequestHandler类并重写handle()方法来创建请求处理程序类; 这个方法将处理传入的请求。
  其次,必须实例化其中一个服务器类,并将其传递给服务器的地址和请求处理程序类。
  然后,调用服务器对象的handle_request()or serve_forever()方法来处理一个或多个请求。
  最后,调用server_close()关闭套接字。
ThreadingTCPServer---->TCPServer  ------>BaseServer------>RequestHandlerClass
|                    (__init__)                            server_forever
|                                                        finish_request
|-----> ThreadingMixIn.process_request()
|----->ThreadingMixIn.process_request_thread()
  内部调用流程为:
  · 启动服务端程序
  · 执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
  · 执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
  · 执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
  · 当客户端连接到达服务器
  · 执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
  · 执行 ThreadingMixIn.process_request_thread 方法
  · 执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass()  即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用 MyRequestHandler的handle方法)
  服务端
# -*- coding: utf-8 -*-
# 2017/11/25 20:15
import socketserver
class MyServer(socketserver .BaseRequestHandler):
def handle(self):
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall(bytes('欢迎致电 10086,0转人工服务.',encoding='utf8'))
Flag = True
while Flag:
data = conn.recv(1024)
data = str(data, encoding='utf8')
if data == 'exit':
Flag = False
elif data == '0':
conn.sendall(bytes('通过可能会被录音',encoding='utf8'))
else:
conn.sendall(bytes('请重新输入.',encoding='utf8'))
if __name__ == '__main__':
server = socketserver .ThreadingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()
  服务端源码模拟
1 # -*- coding: utf-8 -*-
2 # 2017/11/25 20:37
3 import socket
4 import threading
5 import select
6
7
8 def process(request, client_address):
9     print(request,client_address)
10     conn = request
11     conn.sendall(bytes('欢迎致电 10086,请输入1xxx,0转人工服务.', encoding='utf8'))
12     Flag = True
13     while Flag:
14         data = conn.recv(1024)
15         data = str(data, encoding='utf8')
16         if data == 'exit':
17             Flag = False
18         elif data == '0':
19             print(data)
20             conn.sendall(bytes('通过可能会被录音.balabala一大推', encoding='utf8'))
21         else:
22             conn.sendall(bytes('请重新输入.', encoding='utf8'))
23
24 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25 sk.bind(('127.0.0.1',8002))
26 sk.listen(5)
27
28 while True:
29     r, w, e = select.select([sk,],[],[],1)
30     print('looping')
31     if sk in r:
32         print('get request')
33         request, client_address = sk.accept()
34         t = threading.Thread(target=process, args=(request, client_address))
35         t.daemon = False
36         t.start()
37 sk.close()
  如精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 Threading 两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,当前线程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。
  客户端
# -*- coding: utf-8 -*-
# 2017/11/25 20:16
import socket
ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)
while True:
data = sk.recv(1024)
data = str(data, encoding='utf8')
print('receive:',data)
inp = input('please input:')
sk.sendall(bytes(inp,encoding="utf8"))
if inp == 'exit':
break
sk.close()
  ForkingTCPServer只是将 ThreadingTCPServer 实例中的代码:
  SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于 select 和 os.fork 两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,当前新创建的进程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。
  server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyRequestHandler)
  变更为:
  server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyRequestHandler)
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号