– 网络服务器框架 – 互联网协议和支持(Python教程)(参考资料)
socketserver
– 网络服务器框架
源代码:LIB / socketserver.py
socketserver
模块简化了编写网络服务器的任务.
有四个基本的具体服务器类:
- class
socketserver.
TCPServer
(server_address, RequestHandlerClass, bind_and_activate=True) -
这使用Internet TCP协议,它在客户端和服务器之间提供连续的数据流。如果bind_and_activate为true,构造函数会自动尝试//调用
server_bind()
和server_activate()
。其他参数传递到BaseServer
基类.
- 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
.
这四个类处理请求synchronously;在下一个请求开始之前,必须完成每个请求。如果每个请求需要很长时间才能完成,这是不合适的,因为它需要大量计算,或者因为它返回了客户端处理速度慢的大量数据。解决方案是创建一个单独的进程或线程来处理每个请求;ForkingMixIn
和ThreadingMixIn
混合类可用于支持异步行为.
创建服务器需要几个步骤。首先,您必须通过继承BaseRequestHandler
类并覆盖其handle()
方法来创建一个requesthandler类;此方法将处理传入请求。其次,您必须实例化其中一个服务器类,并将其传递给服务器的地址和请求处理程序类。建议在with
语句中使用服务器。然后调用服务器对象的handle_request()
或serve_forever()
方法来处理一个或多个请求。最后,调用server_close()
关闭套接字(除非你使用了with
语句).
继承ThreadingMixIn
对于线程连接行为,你应该明确地声明你希望你的线程如何在abruptshutdown上运行。ThreadingMixIn
class定义一个属性daemon_threads,表示服务器是否应等待提前终止。如果您想要自动行为,您应该明确地设置标志;默认是False
,意思是在ThreadingMixIn
创建的所有线程都没有退出之前,不会退出.
类具有相同的外部方法和属性,无论它们使用什么网络协议.
服务器创建说明
继承图中有五个类,其中四个代表四种类型的同步服务器:
+------------+| BaseServer |+------------+ | v+-----------+ +------------------+| TCPServer |------->| UnixStreamServer |+-----------+ +------------------+ | v+-----------+ +--------------------+| UDPServer |------->| UnixDatagramServer |+-----------+ +--------------------+
注意UnixDatagramServer
派生自UDPServer
,而不是来自UnixStreamServer
– IP和Unixstream服务器之间的唯一区别是地址族,它在两个Unix服务器类中都重复了.
- class
socketserver.
ForkingMixIn
- class
socketserver.
ThreadingMixIn
-
可以使用这些混合类创建每种类型服务器的分叉和线程版本。例如,
ThreadingUDPServer
创建如下:class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
混合类首先出现,因为它覆盖了
UDPServer
中定义的方法。设置各种属性也会改变底层服务器机制的行为.ForkingMixIn
支持fork()
.socketserver.ForkingMixIn.server_close()
的POSIX平台只提供下面提到的分叉类,等待所有子进程完成,除非socketserver.ForkingMixIn.block_on_close
属性为false .socketserver.ThreadingMixIn.server_close()
等待所有非守护线程完成,除非socketserver.ThreadingMixIn.block_on_close
属性为false。通过将ThreadingMixIn.daemon_threads
设置为True
而不等到threadscomplete.
- class
socketserver.
ForkingTCPServer
- class
socketserver.
ForkingUDPServer
- class
socketserver.
ThreadingTCPServer
- class
socketserver.
ThreadingUDPServer
-
这些类是使用混合类预先定义的.
要实现一个服务,你必须从BaseRequestHandler
派生一个类并重新定义它handle()
然后,您可以通过将其中一个服务器类与请求处理程序类组合来运行各种版本的服务。对于数据报或流服务,请求处理程序类必须不同。这可以通过使用处理程序子类StreamRequestHandler
或DatagramRequestHandler
.
来隐藏。当然,你仍然需要使用你的头!例如,如果服务在内存中包含可以由不同请求修改的状态,那么使用分叉服务器是没有意义的,因为子进程中的修改将永远不会达到保持在父进程中并传递给每个子进程的初始状态。在这种情况下,您可以使用线程服务器,但您可能需要使用锁来保护共享数据的完整性.
另一方面,如果您正在构建一个所有数据都存储在内部的HTTP服务器(例如,在文件系统中),同步类将在处理一个请求时威胁性地使服务“失聪” – 这可能是非常如果客户端收到所请求的所有数据的速度很慢,则很长时间这里的线程或分叉服务器是合适的.
在某些情况下,同步处理部分请求可能是合适的,但要根据请求数据完成分叉子进程的处理。这可以通过使用同步服务器并在请求处理程序类handle()
method
//在既不支持线程也不支持fork()
(或者这些太昂贵或不适合服务的地方)是保持一份明确的部分完成请求表并使用selectors
决定下一个要求哪个请求(或者是否处理新的传入请求)。这对于流服务尤为重要,因为每个客户端都可能长时间连接(如果不能使用线程或子进程)。请参阅asyncore
以了解另一种管理方式.
Server Objects
- class
socketserver.
BaseServer
(server_address, RequestHandlerClass) -
这是所有Server对象的超类在模块中。它定义了接口,如下所示,但没有实现大多数方法,这些方法都在子类中。这两个参数存储在各自的
server_address
和RequestHandlerClass
属性中fileno
// ()-
返回一个整数文件描述符服务器正在其上的套接字。这个函数最常传递给
selectors
,以便在同一进程中监视多个服务器.
handle_request
()-
处理单个请求。该函数按顺序调用以下方法:
get_request()
,verify_request()
和process_request()
。如果用户提供的handle()
方法处理程序类引发异常,服务器的handle_error()
方法将被调用。如果timeout
秒,handle_timeout()
将被召唤和handle_request()
将返回。
serve_forever
(poll_interval=0.5)-
处理请求直到明确
shutdown()
请求。每次poll_interval秒轮询forshutdown.Ignorestimeout
属性。它也叫service_actions()
,可以由子类或mixin使用,以提供特定于给定服务的操作。例如,ForkingMixIn
类使用service_actions()
来清理zombiechild进程.在版本3.3中更改:添加
service_actions
打电话给serve_forever
方法。
service_actions
()-
在中调用
serve_forever()
环。这个方法可以被子类或mixin类重写,以执行特定于给定服务的操作,例如清理操作.新版本3.3.
shutdown
()-
告诉
serve_forever()
循环停止并等到它.
server_close
()-
清理服务器。可能被覆盖了
address_family
-
服务器套接字所属的协议族。常见的例子是
socket.AF_INET
和socket.AF_UNIX
.
RequestHandlerClass
-
用户提供的请求处理程序类;为每个请求创建此类的实例.
server_address
-
服务器正在侦听的地址。地址格式因协议族而异;有关详细信息,请参阅
socket
模块的文档。对于Internet协议,这是一个包含给出地址的字符串的元组,以及一个整数端口号:("127.0.0.1", 80)
,例如.
socket
-
服务器将侦听传入请求的套接字对象.
服务器类支持以下类变量:
allow_reuse_address
-
服务器是否允许重用地址。默认为
False
,可以在子类中设置以更改策略.
request_queue_size
-
请求队列的大小。如果处理单个请求需要很长时间,那么在服务器繁忙时到达的任何请求都会被放入队列中,直到
request_queue_size
要求。队列满后,来自客户端的进一步请求将出现“连接被拒绝”错误。defaultvalue通常为5,但是子类可以覆盖它.
socket_type
-
服务器使用的套接字类型;
socket.SOCK_STREAM
和socket.SOCK_DGRAM
是两个常见值.
timeout
-
超时持续时间,以秒为单位,或
None
如果没有超时要求。如果handle_request()
在超时期间没有收到传入的请求,handle_timeout()
方法被调用.
有各种服务器方法可以被像
TCPServer
;这些方法对服务器对象的外部用户没用.finish_request
(request, client_address)-
实际上通过实例化
RequestHandlerClass
来处理请求并调用它handle()
方法。
get_request
()-
必须接受来自套接字的请求,并返回一个2元组,其中包含用于与客户端通信的new套接字对象,以及客户端的地址.
handle_error
(request, client_address)-
如果
handle()
实例的RequestHandlerClass
方法引发异常,则调用此函数。默认操作是打印traceback tostandard错误并继续处理进一步的请求.更改版本3.6:现在只调用从
Exception
class.
handle_timeout
()-
派生的异常。这个函数是当
timeout
属性设置为None
并且超时期限已经过去,并收到了norequests。分叉服务器的默认操作是收集已退出的任何子进程的状态,而在线程服务器中,此方法不执行任何操作.
process_request
(request, client_address)-
调用
finish_request()
创建RequestHandlerClass
的实例。如果需要,此函数可以创建一个新的处理器线程来处理请求;ForkingMixIn
和ThreadingMixIn
课程这样做.
server_activate
( )-
由服务器的构造函数调用以激活服务器。TCP服务器的默认行为只是在服务器的套接字上调用
listen()
。可以被覆盖.
server_bind
()-
由服务器的构造函数调用,将套接字绑定到所需的地址。可以覆盖.
verify_request
(request, client_address)-
必须返回一个布尔值;如果值为
True
,则处理请求,如果是False
,请求将被拒绝。可以重写此函数以实现服务器的访问控制。默认实现总是返回True
.
在版本3.6中更改:添加了对上下文管理器协议的支持。退出上下文管理器相当于调用
server_close()
.
请求处理程序对象
- class
socketserver.
BaseRequestHandler
-
这是所有请求处理程序对象的超类。它定义了接口,如下所示。具体的请求处理程序子类必须定义一个新的
handle()
方法,并且可以覆盖任何其他方法。为每个请求创建一个子类的新实例.setup
()-
在
handle()
方法之前调用以执行任何初始化操作。默认实现什么都不做.
handle
( )-
此功能必须完成服务请求所需的所有工作。默认实现什么都不做。有几个实例属性可供它使用;请求可以
self.request
;clientaddress为self.client_address
;服务器实例为self.server
,以防需要访问每服务器信息.self.request
的类型对于数据报或流服务是不同的。对于流服务,self.request
是套接字对象;fordatagram服务,self.request
是一对字符串和插座.
finish
()-
在
handle()
方法执行之后调用清理行动需要。默认实现什么都不做。如果setup()
引发异常,则不会调用此函数.
- class
socketserver.
StreamRequestHandler
- class
socketserver.
DatagramRequestHandler
-
这些
BaseRequestHandler
子类覆盖setup()
和finish()
方法,并且提供self.rfile
和self.wfile
属性。self.rfile
和self.wfile
属性可分别读取或写入,以获取请求数据或返回客户端数据.两个类的
rfile
属性都支持io.BufferedIOBase
可读界面和DatagramRequestHandler.wfile
支持io.BufferedIOBase
可写界面在版本3.6中更改:
StreamRequestHandler.wfile
也支持io.BufferedIOBase
可写界面.
示例
socketserver.TCPServer
示例
这是服务器端:
import socketserverclass 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.sendall(self.data.upper())if __name__ == "__main__": HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999 with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server: # Activate the server; this will keep running until you # interrupt the program with Ctrl-C server.serve_forever()
一个使用流的替代请求处理程序类(通过提供标准文件接口简化通信的类文件对象):
class MyTCPHandler(socketserver.StreamRequestHandler): def handle(self): # self.rfile is a file-like object created by the handler; # we can now use e.g. readline() instead of raw recv() calls self.data = self.rfile.readline().strip() print("{} wrote:".format(self.client_address[0])) print(self.data) # Likewise, self.wfile is a file-like object used to write back # to the client self.wfile.write(self.data.upper())
区别在于readline()
第二个处理程序中的调用将多次调用recv()
,直到遇到换行符,而单个recv()
第一个处理程序中的调用将只返回从客户端发送的内容sendall()
call
这是客户端:
import socketimport sysHOST, PORT = "localhost", 9999data = " ".join(sys.argv[1:])# Create a socket (SOCK_STREAM means a TCP socket)with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: # Connect to server and send data sock.connect((HOST, PORT)) sock.sendall(bytes(data + "\n", "utf-8")) # Receive data from the server and shut down received = str(sock.recv(1024), "utf-8")print("Sent: {}".format(data))print("Received: {}".format(received))
示例的输出应该看起来像什么像这样:
服务器:
$ python TCPServer.py127.0.0.1 wrote:b"hello world with TCP"127.0.0.1 wrote:b"python is nice"
客户端:
$ python TCPClient.py hello world with TCPSent: hello world with TCPReceived: HELLO WORLD WITH TCP$ python TCPClient.py python is niceSent: python is niceReceived: PYTHON IS NICE
socketserver.UDPServer
示例
这是服务器端:
import socketserverclass MyUDPHandler(socketserver.BaseRequestHandler): """ This class works similar to the TCP handler class, except that self.request consists of a pair of data and client socket, and since there is no connection the client address must be given explicitly when sending data back via sendto(). """ def handle(self): data = self.request[0].strip() socket = self.request[1] print("{} wrote:".format(self.client_address[0])) print(data) socket.sendto(data.upper(), self.client_address)if __name__ == "__main__": HOST, PORT = "localhost", 9999 with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server: server.serve_forever()
这是客户端:
import socketimport sysHOST, PORT = "localhost", 9999data = " ".join(sys.argv[1:])# SOCK_DGRAM is the socket type to use for UDP socketssock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# As you can see, there is no connect() call; UDP has no connections.# Instead, data is directly sent to the recipient via sendto().sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))received = str(sock.recv(1024), "utf-8")print("Sent: {}".format(data))print("Received: {}".format(received))
Asynchronous Mixins
要构建异步处理程序,请使用ThreadingMixIn
和ForkingMixIn
classes.
import socketimport threadingimport socketserverclass ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): def handle(self): data = str(self.request.recv(1024), "ascii") cur_thread = threading.current_thread() response = bytes("{}: {}".format(cur_thread.name, data), "ascii") self.request.sendall(response)class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): passdef client(ip, port, message): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((ip, port)) sock.sendall(bytes(message, "ascii")) response = str(sock.recv(1024), "ascii") print("Received: {}".format(response))if __name__ == "__main__": # Port 0 means to select an arbitrary unused port HOST, PORT = "localhost", 0 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) with server: ip, port = server.server_address # Start a thread with the server -- that thread will then start one # more thread for each request server_thread = threading.Thread(target=server.serve_forever) # Exit the server thread when the main thread terminates server_thread.daemon = True server_thread.start() print("Server loop running in thread:", server_thread.name) client(ip, port, "Hello World 1") client(ip, port, "Hello World 2") client(ip, port, "Hello World 3") server.shutdown()
示例的输出应该如下所示:
$ python ThreadedTCPServer.pyServer loop running in thread: Thread-1Received: Thread-2: Hello World 1Received: Thread-3: Hello World 2Received: Thread-4: Hello World 3
ForkingMixIn
类以相同的方式使用,除了服务器将为每个类生成一个新进程request.Available仅适用于支持fork()
.
评论被关闭。