socketserver
socketserver模块简化了编写网络服务器的任务。
以下有4个基础的服务器类:
-
class
socketserver.TCPServer(server_address, RequestHandle, bind_and_activate=True)
该类使用TCP协议,为服务器与客户端提供连续的数据流。如果bind_and_activate的值为True,构造器将自动尝试调用server_bind()和server_activate()。其他参数将被传递到BaseServer。 -
class
socketserver.UDPServer( server_address, RequestHandle, bind_and_activate=True)
该类使用数据报,它将数据分片打包传送,这可能会导致传送失败或数据丢失。其他参数将被传递到BaseServer。 -
class
socketserver.UnixStreamServer(server_address, RequestHandle, bind_and_activate=True)
-
class
socketserver.UnixDatagramServer(server_address, RequestHandle, bind_and_activate=True)
这两种相对少见的类与TCP和UDP类相似,但是使用的是Unix域套接字,他们无法在非Unix的系统上使用。它们的其他参数与将被传递到BaseServer。这4个类都是同步地处理请求,在下一个请求开始之前,当前请求必须已经结束。这不并适用于耗时很长的那类请求,例如这个请求需要大量计算,或者会返回大量数据给客户端,而客户端又处理很慢。解决方法是为每一个请求创建一个独立的进程或线程,
ForkingMixIn
和ThreadingMixIn
混合类能支持异步的操作。创建一个服务器需要多个步骤。首先,你必须通过创建
BaseRequestHandler
的子类来创建一个请求处理器类,并覆盖他的handle()
方法,此方法将处理收到的请求。第二,你必须创建一个服务器实例,并将服务器地址和请求处理器类传入这个实例。建议使用with方式启用服务器。然后调用该服务器对象的handle_request()
或server_forever()
方法来处理一个或多个请求。最后,调用server_close()
来关闭套接字(如果没有使用with方式)。当继承
ThreadingMixIn
将连接多线程化时,你应该明确地声明让你的线程如何来响应一个突然的中断。ThreadingMixiN
类定义了一个属性daemon_threads
,它表明了服务器是否应该等待线程中止。如果你希望线程自主地运行,你应该设置标识flag;它的默认值是False
,这意味着python将不会退出直到所有由ThreadingMixIn
所创建的线程退出。这些服务器类,不管使用的是哪种网络协议,都有相同的表层方法与属性
1.服务器创建说明
下图中的5个类,其中有4个代表着4种类型的同步服务器:
BaseServer
↓
TCPServer → UnixStreamServer
↓
UDPServer → UnixDatagramServer
IP服务器与Unix服务器间的唯一区别是地址家族,在两个Unix服务器类中重写了地址家族。
class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn
各个类型服务器的Forking和Threading版可以通过以上两个类来创建,例如:class ThreadingUDPServer(ThreadingMixIn, UDPServer)
,mix-in类在前,它重写了UDPServer
的一个方法。设置了个别参数并改变了底层的服务器机制。文中提到的ForkingMixIn类和Forking类只能在支持fork()
方法的POSIX标准的平台上使用。
class socketserver.ForkingTCPServer
class socketserver.ForkingUDPServer
class socketserver.ThreadingTCPServer
class socketserver.ThreadingUDPServer
这些类都是使用mix-in类来预定义生成的类。
为了构建一个服务器,你必须通过BaseRequestHandler
来创建一个请求处理器类,并重写他的handle()
方法。这样你就可以通过组装不同的服务器类与请求处理器类来创建各种版本的服务器。请求处理器类必须区分数据报与流服务器。这个两种类可以通过请求处理器类的子类StreamRequestHandler
与DatagramRequestHandler
来快速创建。
当然,你仍需要动动脑筋!例如,当各种请求可以修改服务器包含的状态时,forking server
是没有意义的,因为子进程中的修改状态的动作将不会传递给父进程,因此也不能将该状态的修改传递给所有子进程。以防万一,你可以使用threading server
,但是你将必须使用锁来保护共享数据的完整。
另一方面,如果你在构建一个HTTP服务器,它的所有数据都存储在外部(例如,在文件系统中)。如果服务器正在处理一个请求,而发起这个请求的客户端接收数据非常缓慢,那这个请求将会耗废很长的时间。若这个服务器使用同步的类来构建的,上述情况将会导致服务器在这段时间内无响应。因此,这种情况下使用多线程或多进程服务器是合适的。
在某些情况中,对于处理一个请求的过程,可能部分过程需要同步进行,但是完成处理却是在一个子进程中,并且依赖于前者的数据。你可以使用同步服务器并在它的处理器类的handle()方法中创建子进程的方式来实现这种需求。
在一个不支持多线程与多进程(或者对服务来说,这些功能过于昂贵或不合适)的环境中,近似于同时处理多个同时发生的请求的方法是维护一个explicit talbe来部分地完成请求,并使用selectors
来决定下一步处理哪个请求(或者是否处理一个新的请求)。这对于那些每一个客户端可能会连接很长时间的流服务来说相当重要(如果threads和subprocesses不能使用)。
2.Server Objects
-
class
socketserver.BaseServer(server_address, RequestHandlerclass)
模块中,这是所有Server objects(服务器对象)的父类。它定义了下面所示的接口,但是其中大多的方法并没有实现,它们需要在子类中重写。两个参数储存在自身的server_address
和RequestHandlerClass
属性中。fileno()
返回正在监听的服务器的套接字的整数文件描述符。这个方法一般被传 递到selectors
,使得可以在同一个进程中监听多个服务器。handle_request()
处理单一请求。这个方法今次调用以下方法:get_request()
,verify_request()
,andprocess_request()
。如果用户提供的请求处理器类的handle()
方法抛出一个异常,服务器将调用handle_error()
。如果在超时时间内没有收到请求,handle_timeout()
方法将被调用并则handle_request()
将返回。serve_forever(poll_intrval=0.5)
持续处理请求走到一个明确的shutdown
请求。每poll_intrval秒轮询查找是否shutdown。忽视timeout属性。它还调用service_action()
方法,一个在子类或混合类中使用来对一个给定的服务器进行操作。例如,ForkingMixIn
类使用该方法来清除僵尸子进程。service_actions()
在serve_forever()
的循环中被调用。这个方法能够被子类或混合类重写,指在对一个给定的服务器进行操作,例如清除操作。shutdown()
让serve_forever()
循环中止并等待。server_close()
退出服务器,可以被重写。address_family
服务器套接字所属的地址家族。一般类似socket.AF_INET
和socket.AF_UNIX
。RequestHandlerClass
用户提供的请求处理类;每个请求都会创建一个此类的实例。server_address
服务器正在监听的地址。地址的格式依据协议家族的不同而不同;参考socket
模块的文档。在Internet协议中,它是一个包含str类型的地址和一个int类型的端口所组成的元组,例如,(’127.0.0.1’,80)。socket
服务器将要监听的接入的请求的socket实例。服务器类支持以下类变量:
allow_reuse_address
服务器是否允许对一个地址重复使用。默认为False,并能在子类中修改这个策略。request_queue_size
请求队列的大小。如果处理一个请求会花费很长时间,那当服务器忙的时候任务接入的请求都将被放入队列中。最多request_queue_size
个请求。当队列已满时,之后从客户端发起的请求将收收到“拒绝连接”错误。默认值 通常为5,能被子类重写。socket_type
服务器的socket的类型;socket.SOCK_STREAM
和socket.SOCK_DGRAM
是两个常用值。timeout
超时时长,单位是秒,如果不需要设置超时,则值为None。如果handle_request()
在超时时间内没有收到请求,handle_timeout()
方法将被调用。以下是一些能够被BaseServer的子类重写的服务器方法,例如TCPServer;这些方法对服务器对象的外部用户是无用的
finish_request(request,client_address)
确实地通过实例化RequestHandlerClass
并调用它的handle()
方法来处理请求。get_request()
必须从socket处接收一个请求,返回一个二元元组,它包含一个用于连接这个客户端的新socket和这个客户端的地址。handle_error(request,client_address)
当一个RequestHandlerClass
实例的handle()
方法抛出异常时,将调用此方法。默认的动作是打印标准错误的追踪并继续处理接下来的请求。handle_timeout()
当timeout
属性已经被设置了值而不是None并且在timeout时间内没有请求抵达时,此方法将被调用。在forking服务器中,默认动作是收集任何一个已经退出的子进程的状态,在threading服务器中,此方法什么都不做。process_request(request,client_address)
调用finish_request()
来创建一个RequestHandlerClass
实例。如果需要,此方法能够创建一个新的进程或线程来处理请求;ForkingMixIn
和ThreadingMixIn
类做了这些。server_activate()
被服务器构造函数调用以激活服务器。默认的在TCP服务器中只是引用服务器套接字的listen()
方法。可以被重写。server_bind()
被服务器构造函数调用来为套接字绑定所需的地址。可以被重写。verify_request(request,client_address)
必须返回一个bool值;如果值为True,请求将被处理,如果是False,请求将被拒绝。这个方法可以被重写来实现服务器的权限控制。默认总是True。
3.Request Handler Objects
-
class
socketserver.BaseRequestHandler
它是所有请求处理器对象的父类。它定义了以下接口。一个具体的请求处理器子类必须定义一个新的handle()
方法,并且可以重写所有其他方法。每个请求都会创建一个新的子类实例。setup()
在handle()
方法之前被调用,用于执行任何需要的初始化工作。默认情况什么都不做。handle()
此函数必须完成服务一个请求的所有工作。默认情况下什么都不做。有好几个实例属性是提供给它的;请求是作为self.request
;客户端地址是self.client_address
;服务器实例化为self.server
,以防它需要访问服务器信息。
self.request
的类型是区别于数据报和流服务的。在流服务中,self.request
是一个socket对象;在数据报服务中,self.request
是一个string和socket元组。finish()
在handle()
方法之后被调用,用于执行任何需要的清理工作。默认什么都不做。如果setup()
抛出一个异常,此函数将不会被调用。 -
class
socketserver.StreamRequestHandler
-
class
socketserver.DatagramRequestHandler
这些BaseRequestHandler
子类重写了setup()
和finish()
方法,并提供了self.rfile
和self.wfile
属性。它们能被读或写,分别用来获取请求数据或返回请求数据至客户端。
这两个类的rfile
属性都支持io.BufferedIOBase
可读接口,DatagramRequestHandler.wfile
支持io.BufferdIOBase
可写接口。版本3.6中,StreamRequestHandler.wfile
与支持io.BufferIOBase
可写接口。