主要类型

  该模块有四个比较主要的类,其中常用的是 TCPServer 和 UDPServer。

  1. TCPServer

  2. UDPServer

  3. UnixStreamServer,类似于TCPServer提供面向数据流的套接字连接,但是旨在UNIX平台上可用;

  4. UnixDatagramServer,类似于UDPServer提供面向数据报的套接字连接,但是旨在UNIX平台上可用;

  这四个类型同步地处理请求,也就是说一个请求没有完成之前是不会处理下一个请求的,这种模式当然不适合生产环境,一个客户端连接就可能拖延所有的执行。所以这个模块还提供了两种支持异步处理的类: 

  5. ForkingMixIn,为每一个客户端请求派生一个新的进程去专门处理;

  6. ThreadingMixIn,为每一个客户端请求派生一个新的线程去专门处理;

  继承自这两个类型的服务端在处理新的客户端连接时不会阻塞,而是创建新的进/线程专门处理客户端的请求。

编程框架 

  首先从高层面介绍一下使用SocketServer模块开发多进程/线程 异步服务器的流程:

  1. 根据需要选择一个合适的服务类型,如,面向TCP连接多进程服务器:  ForkingTCPServer ;

  2. 创建一个请求处理器(request handler)类型,这个类型的 handle()(类似于回调函数)方法中定义如何处理到达的客户端连接。

  3. 实例化服务器,传入服务器绑定的地址和第2步定义的请求处理器类

  4. 调用服务器实例的 handle_request() 或 serve_forever() 方法,一次或多次处理客户请求。

具体流程

1. 选择合适的服务器类型

  上文介绍了SocketServer模块提供的几种主要的基本服务类型和两种MixIn类型,就是构造适合需求的多线程/进程服务器的原料。是否使用、如何使用这两个MixIn类型是由程序员决定的,利用的就是Python的多重继承机制,将MixIn类型作为代码库(而不是初始化实例的工具),为实例提供新的方法。

  SocketServer模块提供的主要服务类型和两种MixIn类型,可以有以下的组合:

 

TCPServer

UDPServer
ForkingMixIn ForkingTCPServer

ForkingUDPServer

ThreadingMixIn ThreadingTCPServer

ThreadingUDPServer

  只要根据需要选择特定类型的server类型即可(例如 ThreadingTCPServer,面向TCP连接的多线程服务器),即便自己定义多进程/线程的server类型,也不过多重继承对应的连接server类和MinIn而已。比如,面向TCP连接线程式异步服务器类型,实际上就是:

class ForkingTCPServer(FrokingMixIn, TCPServer):
pass

  Python 的多重继承机制保证了这里只要继承了必要的父类就可以完成目标类型的定义,不需要添加额外的内容。

2. 定义请求处理器

  SocketServer模块提供了 BaseRequestHandler 类型用于定制Handler类型,自定义的Handler类型只要继承自 BaseRequestHandler 并覆写它的 handle() 方法即可。handle() 方法定义如何处理客户端的请求,服务器只是封装了socket对象的众多操作流程以及进程、线程等的管理,然后对于每一个客户端请求调用handle() 方法。handle() 方法就是server为client创建新的线程后调用的回调函数。

  BaseRequestHandler 实例的一些属性非常有用,可以用来获得一些和连接相关的信息,包括客户端套接字的地址、服务端当前连接的socket对象等:

(1)获取client端socket对象的地址

h.client_address

  h.client_address 是 client 的地址,IPv4地址族中就是 (host, port) 二元组。该属性由基类在连接建立时设置。

(2)获取创建自己的 server 对象

h.server

  该属性保存创建这个 BaseRequestHandler 实例的 server 对象。

(3)从 BaseRequestHandler 实例获取连接套接字

h.request
  • 对 TCP server,h.request 属性是连接到 client 的连接套接字对象;
  • 对 UDP server,h.request 属性是一个二元组(data, sock),data 是 client 端发送的数据(最大8192字节),sock是server端套接字。

  使用这个属性可以获取在这个进/线程中与client套接字建立连接的连接套接字,从而可以使用这个套接字与client端通信。

   StreamRequestHandler 和 DatagramRequestHandler 则屏蔽了 self.request 对TCP和UDP连接的区别,二者都重定义了 setup() 和 finish() 方法,提供统一的 self.rfile 和 self.wfile 属性以便从客户端读取数据或向客户端发送数据。

  BaseRequestHandler 实例 h 提供如下的接口,他们都可以根据需要重写:

(1)初始化

BaseRequestHandler.setup()

  在 handle() 方法之前调用 setup() ,完成一些初始化的工作,默认什么也不做。

  

(2)回调函数 handle()

BaseRequestHandler.handle()

  handle() 完成所有的对于每个请求的处理工作,也就是实现服务端的业务逻辑,默认情况下什么也不做。要与 client 端通信,最终还是要通过建立连接的套接字对象,这里可以使用 h.request 属性获取连接套接字,对于TCP服务器和UDP服务器的区别,参考 h.request 属性。

  

(3)终止化

BaseRequestHandler.finish()

  作用:在handle() 方法之后调用,完成一些清理的工作,默认的情形下什么也不做。如果setup()方法抛出异常,那么该方法不会被调用。

 3. 实例化服务器

  实例化服务器时传入服务器需要绑定的地址是必要的,另一方面还应该传入自定义的Handler类型,服务器实例将对每一个客户端连接调用它的 handle() 方法。

  例如:

server = ForkingTCPServer((host, port), MyRequestHandler)

4. 调用服务器实例的处理方法

  服务器实例的 handle_request() 方法与 serve_forever() 方法分别用于单次处理或一直处理请求,可以直接在脚本中调用这些方法,也可以在新的进程或者线程中调用这些方法,启动服务器:

import threading

...

server = ForkingTCPServer((host, port), MyRequestHandler)
server_thread = threading.Thread(target = server.serve_forever)
server_thread.start()

  则在一个新的线程中创建一个 多进程的TCP服务器,每当一个新的连接到来,他都会创建一个新的进程去服务client端的请求。

实例:

  该例子使用 SocketServer 模块实现一个简单的多线程 TCP 服务器

import SocketServer
class EchoHandler(SocketServer.BaseRequestHandler):
def handle(self):
print("Connected from: ", self.client_address)
while True:
recvData = self.request.recv(1024)
if not recvData:
break
self.request.sendall(recvData)
self.request.close()
print("Disconnected from: ", self.client_address) srv = SocketServer.ThreadingTCPServer(("", 4424), EchoHandler)
srv.serve_forever()

  该例在当前线程中创建一个多线程TCP服务器,其功能是将客户端发送的数据回显给客户。

  可见,在handle()中定义服务器的业务逻辑,任何需要对连接socket对象的操作,都可以通过 self.request 属性操作,在TCP连接中,这个属性就是server端的socket对象。在定义完Handler类型后,将其作为参数传给选择的server类型即可。

  该多线程TCP server 的运行示例:

('Connected from: ', ('127.0.0.1', 63235))
('Connected from: ', ('127.0.0.1', 63236))
('Disconnected from: ', ('127.0.0.1', 63235))
('Disconnected from: ', ('127.0.0.1', 63236))

  这里发起两个客户端同时请求服务端,发现服务端确实具有了基本的并发能力。


补充:

  SocketServer 中的 TCPServer、UDPServer 提供的可供使用的属性、方法(实际上都来自于其父类 SocketServer.BaseServer ):

属性:

BaseServer.address_family

  内容:服务器套接字对象的地址族,如 socket.AF_INET 、 socket.AF_UNIX 等。

BaseServer.RequestHandlerClass

  内容:用户自定义,传给服务器构造函数Handler类型,服务器会为每一个请求创建一个该类型的实例。

BaseServer.server_address

  内容:服务器监听的地址,具体的形式依赖于地址族,如AF_INET形式的 ('127.0.0.1', 80) 等。

BaseServer.socket

  内容:服务器监听的套接字对象

BaseServer.allow_reuse_address

  内容:是否允许地址重用,默认为False,子类可以更改。

BaseServer.request_queue_size

  内容:请求队列的长度,一旦等待服务的请求数达到这个限制,后续到来的请求收到“Connection denied”错误,通常该值默认为5,子类可以覆写。

  

BaseServer.socket_type

  内容:服务器所用套接字的类型,如 socket.SOCK_STREAM 和 socket.SOCK_DGRAM 。

BaseServer.timeout

  内容:服务器的超时限制,如果是None,那么没有设置超时;如果在指定的时限内 handle_request() 方法没有获得请求,那么将会调用 handle_timeout() 方法。

BaseServer.fileno()

  内容:返回服务器所用套接字对象的fd,一种典型的用法是传给 select.select() 方法便于在一个进程中监控多个服务器。

方法:

BaseServer.server_bind()

  作用:由实例的构造函数调用,将套接字绑定到目标地址,可以覆写。

BaseServer.server_activate()

  作用:由实例的构造函数调用,监听服务器套接字,可以覆写。

BaseServer.handle_request()

  作用:处理单个请求,依次调用 get_request() 、 verify_request() 和 process_request() 方法,如果用户自定义的handle()方法抛出异常,则调用 handle_error() 方法,如果超过 self.timeout 秒没有获得请求,调用 handle_timeout() 方法,然后 handle_request() 方法返回。

   BaseServer.get_request()

    作用:服务器对象的使用者不一定需要直接调用该方法,从套接字接受一个请求。返回一个二元组,首元是新的已经连接的套接字对象,次元是客户端的地址。

   BaseServer.verify_request(request, client_address)

    作用:服务器对象的使用者不一定需要直接调用该方法。返回一个布尔值,为True时处理请求,为False时拒绝请求,可以通过覆写该方法为服务器实现访问控制。默认的实现总是返回True。

   BaseServer.process_request(request, client_address)

    作用:该方法体现MixIn的作用,此处可能会创建线程或进程来处理用户的请求,最终都是该方法通过调用 finish_request() 来为每个请求实例化一个用户自定义的Handler实例,然后投到新建的线程或进程中,处理用户的请求。

     BaseServer.finish_request()

      作用:为每个请求实例化一个用户自定义的Handler实例,并调用其 handle() 方法。

   BaseServer.handle_error(request, client_address)

    作用:当用户自定义的Handler实例的 handle() 方法抛出异常时,调用该方法。默认的工作是将traceback打印到标准输出,然后继续处理请求。

   BaseServer.handle_timeout()

    作用:当self.timeout属性规定的超时上限达到时(不是None)还没有等到请求。多进程服务器的默认行为是收集所有已经退出的子进程的状态,多线程服务器默认什么也不做。

BaseServer.serve_forever(poll_interval=0.5)

  作用:一直处理请求,直到显式调用 shutdown() 函数,每隔 poll_interval (默认0.5)秒轮询一遍shutdown,该函数无视 self.timeout 。

BaseServer.shutdown()

  作用:停止 serve_forever() 循环直到其停止。


Contact_me: darren_wang_a^t_outlook.com

Python网络编程(3)——SocketServer模块与简单并发服务器的更多相关文章

  1. SocketServer模块与简单并发服务器

    思维导图文件:https://files-cdn.cnblogs.com/files/benjieming/SocketServer%E6%A8%A1%E5%9D%97%E4%B8%8E%E7%AE% ...

  2. python网络编程socket /socketserver

    提起网络编程,不同于web编程,它主要是C/S架构,也就是服务器.客户端结构的.对于初学者而言,最需要理解的不是网络的概念,而是python对于网络编程都提供了些什么模块和功能.不同于计算机发展的初级 ...

  3. 网络编程 多线程/socketserver模块/ threading.local

    线程:进程中负责程序执行的执行单元. 多线程:在1个进程中存在多个线程. 进程只是用来把资源集中在一起,而线程才是cpu上的执行单位. 每个进程都会默认有一个控制线程也叫作主线程. 进程之间是竞争关系 ...

  4. Python网络编程(socketserver、TFTP云盘、HTTPServer服务器模型)

    HTTP协议? HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型.HTTP是一个无状态的协议. 通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了 ...

  5. Python网络编程(socket模块、缓冲区、http协议)

      网络的概念:主机   端口  IP  协议 服务器: localhost/127.0.0.1 客户端: 只是在本机启动客户端,用127.0.0.1访问     服务器: 0.0.0.0 客户端: ...

  6. python网络编程:socketserver的基本使用

    本文内容: socketserver的介绍 socketserver的使用 socketserver的异步服务端 首发时间:2018-03-21 也可以使用socketserver来创建socket ...

  7. Python网络编程基础 struct模块 解决黏包问题 FTP

    struct模块 解决黏包问题 FTP

  8. Python网络编程基础 ❸ struct模块 基于upd的socket服务

    struct模块 基于upd的socket服务

  9. python网络编程-01

    python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...

随机推荐

  1. 如何开启红米手机4X的ROOT超级权限

    红米手机4X通过什么方法拥有了root权限?大家都清楚,Android机器有root权限,如果手机拥有了root相关权限,可以实现更强的功能,举个栗子大家公司的营销部门同事,使用大多数营销软件都需要在 ...

  2. C#中的yield return用法演示源码

    下边代码段是关于C#中的yield return用法演示的代码. using System;using System.Collections;using System.Collections.Gene ...

  3. C++ 死循环在语言层面的检测

    英文概念 Infinite loop without side-effects 这个目前只有CLang实现了这个C++特色 #include <iostream> int 费马定理() { ...

  4. shell判断USB接口是否有设备插入

    #/bin/sh usb_num=$(cat /proc/scsi/scsi | grep "Vendor" | wc -l)if [ $usb_num = 2 ];then    ...

  5. 06-Nodejs介绍

    06-Nodejs介绍 打开Nodejs英文网:https://nodejs.org/en/ 中文网:http://nodejs.cn/ 我们会发现这样一句话: 翻译成中文如下: Node.js 是一 ...

  6. MySQL InnoDB 日志管理机制中的MTR和日志刷盘

    1.MTR(mini-transaction) 在MySQL的 InnoDB日志管理机制中,有一个很重要的概念就是MTR.MTR是InnoDB存储擎中一个很重要的用来保证物理写的完整性和持久性的机制. ...

  7. mysql 从一个表中查询,插入到另一个表中

    insert into table1(field1) select field1 from table2; ;

  8. python + PyQt5 实现 简易计算器

    忽然想起之前一直想写个简单的计算器,今天就写了一下,界面有些简陋,但是基本功能实现没有问题 以下是源码: # --*-- coding:utf-8 --*-- import sys from PyQt ...

  9. JDBC获得连接时报connection refused

    1,检查数据库服务器的IP是否正确. 2,检查用户名密码是否正确. 3,检查SID,获selecte instance_name from v$instance;

  10. Windows操作系统分类

    Windows主要有桌面版和服务器版.移动版三个版本 桌面版现在主流是WindowsXP.WindowsVista.Windows7.Windows8.Windows10 其中WindowsXP已经被 ...