SocketServer vereinfacht das Schreiben von Netzwerkservern. Es gibt 4 Klassen: TCPServer, UDPServer, UnixStreamServer, UnixDatagramServer. Diese vier Klassen werden synchron verarbeitet und die asynchrone Verarbeitung wird durch die Klassen ForkingMixIn und ThreadingMixIn unterstützt.
Schritte zum Erstellen eines Servers. Zunächst müssen Sie eine Anforderungsverarbeitungsklasse erstellen, die BaseRequestHandler unterordnet und dessen handle()-Methode überschreibt. Zweitens müssen Sie eine Serverklasse instanziieren und dabei die Adresse des Servers und die Anforderungshandlerklasse übergeben. Rufen Sie abschließend handle_request() auf (normalerweise werden andere Ereignisschleifen aufgerufen oder select() verwendet) oder dienen_forever().
Bei der Integration der ThreadingMixIn-Klasse muss das Schließen von Ausnahmen behandelt werden. daemon_threads gibt an, ob der Server auf die Beendigung des Threads warten möchte. Wenn die Threads unabhängig voneinander sind, muss der Wert auf „True“ gesetzt werden.
Unabhängig davon, welches Netzwerkprotokoll verwendet wird, verfügt die Serverklasse über dieselben externen Methoden und Eigenschaften.
In Python3 ist dieses Modul das Socketserver-Modul. In Python 2 ist dieses Modul das SocketServer-Modul. Daher müssen Sie beim Importieren mit Import den Import entsprechend der Situation durchführen, andernfalls wird ein Fehler gemeldet. Der importierte Code lautet wie folgt:
try: import socketserver #Python 3 except ImportError: import SocketServer #Python 2
Das SocketSerror-Modul enthält viele Klassen, die die Implementierung von TCP-, UDP- und UNIX-Domänen-Socket-Servern vereinfachen.
1. Verarbeitungsvorgang
Um dieses Modul zu verwenden, müssen Sie eine Handlerklasse definieren, die von der Basisklasse BaseRequestHandler erbt. Instanz h der BaseRequestHandler-Klasse kann die folgenden Methoden implementieren:
1. h.handle() Rufen Sie diese Methode auf, um die eigentliche Anforderungsoperation auszuführen. Diese Funktion kann ohne Parameter aufgerufen werden, mehrere Instanzvariablen enthalten jedoch nützliche Werte. h.request enthält die Anfrage, h.client_address enthält die Clientadresse und h.server enthält die Instanz, die den Handler aufgerufen hat. Bei Daten-Streaming-Diensten wie TCP ist das h.request-Attribut das Socket-Objekt. Bei Datagrammdiensten handelt es sich um eine Bytefolge, die die empfangenen Daten enthält.
2. h.setup() Diese Methode wird vor handle() aufgerufen. Standardmäßig passiert nichts. Wenn Sie möchten, dass der Server weitere Verbindungseinstellungen vornimmt (z. B. den Aufbau einer SSL-Verbindung), können Sie dies hier tun.
3. h.finish() Rufen Sie diese Methode auf, um den Bereinigungsvorgang nach der Ausführung von handle() durchzuführen. Standardmäßig passiert nichts. Wenn weder die Methoden setup() noch handle() eine Ausnahme erzeugen, besteht keine Notwendigkeit, diese Methode aufzurufen.
Wenn Sie wissen, dass Ihre Anwendung nur Stream-orientierte Verbindungen (z. B. TCP) manipulieren kann, sollten Sie von StreamRequestHandler anstelle von BaseRequestHandler erben. Die StreamRequestHandler-Klasse legt zwei Attribute fest. h.wfile ist ein dateiähnliches Objekt, das Daten auf den Client schreibt, und h.rfile ist ein dateiähnliches Objekt, das Daten vom Client liest.
Wenn Sie einen Handler schreiben möchten, der Pakete bearbeitet und die Antwort kontinuierlich an den Absender zurückgibt, sollte er von DatagramRequestHandler erben. Die bereitgestellte Klassenschnittstelle ist dieselbe wie StramRequestHandler.
2. Server
Um einen Handler zu verwenden, muss dieser in das Serverobjekt eingefügt werden. Es sind vier grundlegende Serverklassen definiert.
(1) TCPServer(Adresse,Handler) unterstützt den Server, der das TCP-Protokoll von IPv4 verwendet. Adresse ist ein (Host, Port)-Tupel. Handler ist eine Instanz einer Unterklasse der BaseRequestHandler- oder StreamRequestHandler-Klasse.
(2) UDPServer(address,handler) unterstützt den Server, der das UDP-Protokoll von IPv4 verwendet. Die Adresse und der Handler ähneln denen in TCPServer.
(3) UnixStreamServer (Adresse, Handler) verwendet UNIX-Domänen-Sockets, um einen datenstromprotokollorientierten Server zu implementieren, der von TCPServer geerbt wird.
(4) UnixDatagramServer(address,handler) Ein Server, der UNIX-Domänensockets verwendet, um das von UDPServer geerbte Datagrammprotokoll zu implementieren.
Instanzen aller vier Serverklassen verfügen über die folgenden Methoden und Variablen:
1. s.socket Das Socket-Objekt, das für eingehende Anfragen verwendet wird.
2. s.sever_address Die Adresse des lauschenden Servers. Zum Beispiel Tupel ("127.0.0.1",80)
3. s.RequestHandlerClass Die Anforderungshandlerklasse, die an den Serverkonstruktor übergeben und vom Benutzer bereitgestellt wird.
4. s.serve_forever() verarbeitet unbegrenzte Anfragen
5. s.shutdown() Stoppen Sie die Serve_forever()-Schleife
6. s.fileno() Gibt den ganzzahligen Dateideskriptor des Server-Sockets zurück. Diese Methode nutzt Serverinstanzen effektiv durch Abfragevorgänge wie die Funktion select().
三、定义自定义服务器
服务器往往需要特殊的配置来处理不同的网络地址族、超时期、并发和其他功能,可以通过继承上面四个基本服务器类来自行定义。
可以通过混合类获得更多服务器功能,这也是通过进程或线程分支添加并发行的方法。为了实现并发性,定义了以下类:
(1)ForkingMixIn 将UNIX进程分支添加到服务器的混合方法,使用该方法可以让服务器服务多个客户。
(2)ThreadingMixIn 修改服务器的混合类,可以使用线程服务多个客户端。
要向服务器添加这些功能,可以使用多重继承,其中首先列出混了类。
由于并发服务器很常用,为了定义它,SocketServer预定义了以下服务器类:
(1)ForkingUDPServer(address,handler)
(2)ForkingTCPServer(address,handler)
(3)ThreadingUDPServer(address,handler)
(4)ThreadingTCPServer(address,handler)
上面有点乱,现总结以下:
SocketServer模块中的类主要有以下几个:
1、BaseServer 包含服务器的核心功能与混合类(mix-in)的钩子功能。这个类主要用于派生,不要直接生成这个类的类对象,可以考虑使用TCPServer和UDPServer类。
2、TCPServer 基本的网络同步TCP服务器
3、UDPServer 基本的网络同步UDP服务器
4、ForkingMixIn 实现了核心的进程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
5、ThreadingMixIn 实现了核心的线程化功能,用于与服务器类进行混合(mix-in),以提供一些异步特性。不要直接生成这个类的对象。
6、ForkingTCPServer ForkingMixIn与TCPServer的组合
7、ForkingUDPServer ForkingMixIn与UDPServer的组合
8、BaseRequestHandler
9、StreamRequestHandler TCP请求处理类的一个实现
10、DataStreamRequestHandler UDP请求处理类的一个实现
现在繁杂的事务都已经封装到类中了,直接使用类即可。
四、实例
1.使用SocketServer模块编写的TCP服务器端代码:
#! /usr/bin/env python #coding=utf-8 """使用SocketServer来实现简单的TCP服务器""" from SocketServer import (TCPServer,StreamRequestHandler as SRH) from time import ctime class MyRequestHandler(SRH): def handle(self): print "connected from ",self.client_address self.wfile.write("[%s] %s" %(ctime(),self.rfile.readline())) tcpSer=TCPServer(("",10001),MyRequestHandler) print "waiting for connection" tcpSer.serve_forever() 相应的TCP客户端代码: #! /usr/bin/env python #coding=utf-8 from socket import * BUFSIZE=1024 #每次都要创建新的连接 while True: tcpClient=socket(AF_INET,SOCK_STREAM) tcpClient.connect(("localhost",10001)) data=raw_input(">") if not data: break tcpClient.send("%s\r\n" %data) data1=tcpClient.recv(BUFSIZE) if not data1: break print data1.strip() tcpClient.close()
2.异步服务器的实现
ThreadingMixIn的例子:
import socketimport threadingimport SocketServerclass ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): def handle(self): data = self.request.recv(1024) cur_thread = threading.current_thread() response = "{}: {}".format(cur_thread.name, data) self.request.sendall(response)class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): passdef client(ip, port, message): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) try: sock.sendall(message) response = sock.recv(1024) print "Received: {}".format(response) finally: sock.close()if __name__ == "__main__": # Port 0 means to select an arbitrary unused port HOST, PORT = "localhost", 0 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) 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.py Server loop running in thread: Thread-1 Received: Thread-2: Hello World 1 Received: Thread-3: Hello World 2 Received: Thread-4: Hello World 3