Home > Article > Backend Development > Tutorial on writing basic server programs in Python using the SocketServer module
SocketServer simplifies writing network servers. It has 4 classes: TCPServer, UDPServer, UnixStreamServer, UnixDatagramServer. These four classes are processed synchronously, and asynchronous is supported through the ForkingMixIn and ThreadingMixIn classes.
Steps to create a server. First, you must create a request handling class that subclasses BaseRequestHandler and overrides its handle() method. Second, you must instantiate a server class, passing in the server's address and request handler class. Finally, call handle_request() (usually calling other event loops or using select()) or serve_forever().
Exception shutdown needs to be handled when integrating the ThreadingMixIn class. daemon_threads indicates whether the server wants to wait for the thread to terminate. If the threads are independent of each other, it must be set to True. The default is False.
No matter what network protocol is used, the server class has the same external methods and properties.
In Python3, this module is the socketserver module. In Python 2, this module is the SocketServer module. Therefore, when importing using import, you must import according to the situation, otherwise an error will be reported. The imported code is as follows:
try: import socketserver #Python 3 except ImportError: import SocketServer #Python 2
The SocketSerror module includes many classes that simplify TCP, UDP, and UNIX domain socket server implementations.
1. Processing procedure
To use this module, you must define a handler class that inherits from the base class BaseRequestHandler. Instance h of the BaseRequestHandler class can implement the following methods:
1. h.handle() Call this method to perform the actual request operation. This function can be called without any parameters, but several instance variables contain useful values. h.request contains the request, h.client_address contains the client address, and h.server contains the instance that called the handler. For data streaming services such as TCP, the h.request attribute is the socket object. For datagram services, it is a byte string containing the received data.
2. h.setup() This method is called before handle(). By default, it does nothing. If you want the server to implement more connection settings (such as establishing an SSL connection), you can do so here.
3. h.finish() Call this method to perform the cleanup operation after executing handle(). By default, it does nothing. If neither the setup() nor handle() methods generate an exception, there is no need to call this method.
If you know that your application can only manipulate stream-oriented connections (such as TCP), you should inherit from StreamRequestHandler instead of BaseRequestHandler. The StreamRequestHandler class sets two attributes. h.wfile is a file-like object that writes data to the client, and h.rfile is a file-like object that reads data from the client.
If you want to write a handler that operates on packets and continuously returns the response to the sender, it should inherit from DatagramRequestHandler. The class interface it provides is the same as StramRequestHandler.
2. Server
To use a handler, it must be inserted into the server object. Four basic server classes are defined.
(1) TCPServer(address,handler) supports the server using the TCP protocol of IPv4. Address is a (host, port) tuple. Handler is an instance of a subclass of the BaseRequestHandler or StreamRequestHandler class.
(2) UDPServer(address,handler) supports the server using the UDP protocol of IPv4. The address and handler are similar to those in TCPServer.
(3) UnixStreamServer(address,handler) uses UNIX domain sockets to implement a data stream protocol-oriented server, inherited from TCPServer.
(4) UnixDatagramServer(address,handler) A server that uses UNIX domain sockets to implement the datagram protocol, inherited from UDPServer.
Instances of all four server classes have the following methods and variables:
1. s.socket The socket object used for incoming requests.
2. s.sever_address The address of the listening server. Such as tuple ("127.0.0.1",80)
3. s.RequestHandlerClass The request handler class passed to the server constructor and provided by the user.
4. s.serve_forever() handles unlimited requests
5. s.shutdown() Stops the serve_forever() loop
6. s.fileno() Returns the integer file descriptor of the server socket. This method effectively uses server instances through polling operations such as the select() function.
三、定义自定义服务器
服务器往往需要特殊的配置来处理不同的网络地址族、超时期、并发和其他功能,可以通过继承上面四个基本服务器类来自行定义。
可以通过混合类获得更多服务器功能,这也是通过进程或线程分支添加并发行的方法。为了实现并发性,定义了以下类:
(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