socketserver模块简化了网络编程,模块下有五个服务类:BaseServer、TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer 。这五个类的关系以下:html
+------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer |
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True):TCP数据流服务器
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True):UDP数据报服务器python
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True):仅限于Unix系统的,Unix套接字流
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True):仅限于Unix系统的,Unix数据报编程
他们的参数意义相同,以下:服务器
server_address:服务器的地址,他应该是一个元组包含地址和端口如:('192.168.1.1',9000),网络
RequestHandlerClass:咱们自定义的类,类中必须重写handle()方法。用于处理全部socket请求。多线程
bind_and_activate:若是为True,将自动调用server_bind()和server_activate()。框架
这四个类运行时只能处理一个请求,也就是一个服务端只能对应一个客户端,这对于咱们未来要编写的FTP服务器可能不适用,由于咱们但愿一个服务能处理多个客户端,下面咱们来看socketserver为咱们提供的两个处理异步的类。异步
class socketserver.ForkingMixIn:启用多进程
class socketserver.ThreadingMixIn:启用多线程socket
建立异步服务的方法很是简单,下面建立一个简单的TCP框架为例:函数
import socketserver class MyTCPServer(socketserver.BaseRequestHandler): # 自定义TCP服务类,必须继承socketserver.BaseRequestHandler def handle(self): # 重写此类, ''' 咱们要处理的任务 :return: ''' self.fun() # 执行咱们的任务 def fun(self): print('Hello World') class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): # 类名随便起,必需要继承这两个类 pass if __name__ == "__main__": ip_port = ("127.0.0.1", 9000) # TCP服务器的地址和端口 with socketserver.ThreadingTCPServer(ip_port, MyTCPServer) as server: server.serve_forever() # 开启TCP服务
这样咱们就简单的建立了一个异步TCP服务器框架,其实ThreadingTCPServer这个类咱们不用本身建立,由于socketserver已经为咱们建立好了,以下:
class ForkingUDPServer(ForkingMixIn, UDPServer): pass # 多进程UDP服务器 class ForkingTCPServer(ForkingMixIn, TCPServer): pass # 多进程TCP服务器 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass # 多线程UDP服务器 class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass # 多线程TCP服务器
若是以为socketserver提供的这四个类不是你想要的,那么你就能够像上面那样本身定义,上面都是服务类,经过服务类实例化对象,可是目前还不知道对象拥有哪些方法,由于这些服务类都是继承的BaseServer类,因此方法都在BaseServer类中,有些方法只是定义了接口,在子类中实现的。
class socketserver.BaseServer(server_address, RequestHandlerClass):
fileno():返回服务器正在监听的套接字的文件描述符(int类型的数字)。此函数最常传递给选择器,以容许监视同一进程中的多个服务器。
handle_request():处理单个请求。此函数按顺序调用如下方法:get_request()、verify_request()和process_request()。若是handler类的用户提供的handle()方法引起异常,则将调用服务器的handle_error()方法。若是在超时秒内未收到请求,将调用handle_timeout(),并返回handle_request()。
serve_forever(poll_interval=0.5):定时任务,一般是在一个线程中,每poll_interval秒轮询一次,直到调用shutdown中止。
service_actions():该函数被serve_forever定时函数重复调用,这个方法咱们能够继承BaseServer,而后重写此方法。
shutdown():此方法用于中止serve_forever()定时任务。
socket:socket对象。
socket_type:socket套接字类型,TCP,UDP等。
allow_reuse_address:服务器是否容许地址的重用。默认为false ,而且可在子类中更改。
address_family:设置socket套接字家族。
server_address:值是一个元组,socket服务器地址和监听的端口。
server_activate():服务器将处于监听状态,该函数可被重写,其实他的内部就是self.socket.listen(self.request_queue_size)。
server_bind():将socket绑定到地址上,能够被重写。
get_request():此方法的前提是必须接收到来自套接字的请求,返回一个元组(与客户端通讯的新套接字对象,客户端地址)。其实该方法就是将self.socket.accept()的结果返回。
server_close():关闭服务(关闭socket),此方法可被重写。
RequestHandlerClass:值是类名,这个类是咱们定义的用于建立实例处理咱们的请求,如上面TCP异步框架中的MyTCPServer,这个类就是RequestHandlerClass的值。
request_queue_size:请求队列的大小。若是处理单个请求须要很长时间,在服务器繁忙时会将请求放到队列中,当请求数达到request_queue_size的值时。来自客户端的进一步请求将获得一个“拒绝链接”错误。默认值一般是5,可是这个值能够被子类覆盖。
finish_request(request, client_address):此方法会实例化RequestHandlerClass并调用它的handle()方法来实际处理请求。
process_request(request,client_address):调用finish_request()来建立RequestHandlerClass的一个实例。咱们能够本身建立线程池或进程池来调用这个函数,实现服务器处理多个请求的问题,ForkingMixIn和ThreadingMixIn类就是这样作的。
handle_error(request,client_address):若是RequestHandlerClass实例的handle()方法引起异常,则调用此函数。默认操做是将回溯打印到标准错误,并继续处理其余请求。在版本3.6中更改:如今仅调用从Exception类派生的异常。
timeout:超时时间(以秒为单位),若是是None,会一直阻塞。若是设置了timeout,handle_request()在超时期间没有收到传入请求,则调用handle_timeout()方法。
handle_timeout():当timeout属性被设置为None之外的值,而且超时周期已通过去而没有收到任何请求时,将调用此函数。多进程 服务器的默认操做是收集已退出的任何子进程的状态,而在线程服务器中,此方法不执行任何操做。
verify_request(request,client_address):返回一个布尔值;若是值为真,请求将被处理,若是值为假,请求将被拒绝。能够重写此函数以实现服务器的访问控制。默认实现只是一句return True。
上面这些都是服务对象的方法,下面来介绍处理socket请求类BaseRequestHandler。
class socketserver.BaseRequestHandler:
这是全部socket请求处理程序的基类。它只定义了接口,而没有实现,若是想要使用接口,咱们首先继承BaseRequestHandler,而后在子类中重写这些方法。每一个socket请求处理程序子类必须重写handle()方法,由于该方法是用于处理全部socket请求。该类的方法以下:
setup():在handle()方法以前调用,执行初始化操做。 默认不执行任何操做,咱们能够重写此方法来实现程序的初始化。
handle():全部socket请求任务都是在这个函数内部完成的,咱们在子类中必须重写此方法,并处理socket请求,由于默认基类中的handle()的实现不执行任何操做。
finish():在handle()方法以后调用以执行清理操做。默认实现不执行任何操做。若是setup()引起异常,则不会调用此函数。
虽然上面的接口都只是定义而没有实现,可是它的实例属性仍是颇有用的;
self.request;客户端和服务端的链接对象,用于发送数据,接收数据。
self.client_address:socket客户端地址 。
self.server:socket服务端信息。
class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler
这两个类是socketserver继承BaseRequestHandler后重写了setup(),finish(),实现了对读,写缓冲区的设置,有兴趣的能够看看源码。
TCP同步服务示例:
# 服务端 import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): # 自定义类,继承BaseRequestHandler,处理socket请求 def handle(self): # socket客户端请求 self.data = self.request.recv(1024).strip() # 接收socket客户端发来的数据 print("{} wrote:".format(self.client_address[0])) print(self.data) self.request.sendall(self.data.upper()) # 将数据大写后发给客户端 ''' class MyTCPHandler(socketserver.StreamRequestHandler): # 自定义类,功能与上面的同样,只不过是继承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()) ''' if __name__ == "__main__": HOST, PORT = "localhost", 9999 with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server: server.serve_forever() # 启用TCP服务器 # 打印内容以下 127.0.0.1 wrote: b'Hello World' # 客户端 import socket import sys HOST, PORT = "localhost", 9999 data = "Hello World" # 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)) # 打印内容以下 Sent: Hello World Received: HELLO WORLD
UDP服务示例:
# 服务端 import socketserver class 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() # 打印内容以下 127.0.0.1 wrote: b'Hello World' # 客户端 import socket import sys HOST, PORT = "localhost", 9999 data = "Hello World" # SOCK_DGRAM is the socket type to use for UDP sockets sock = 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)) # 打印内容以下 Sent: Hello World Received: HELLO WORLD
TCP服务异步示例:
import socket import threading import socketserver class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): # 自定义socket请求处理类 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): # 自定义线程类处理多个请求 pass def client(ip, port, message): ''' socket客户端 :param ip: 服务段的IP地址 :param port: 服务端的端口 :param message: 给服务端发送的消息 :return: ''' 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__": HOST, PORT = "localhost", 0 # 端口是0随机获取一个未被使用的端口 server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler) with server: ip, port = server.server_address # 获取服务端的IP地址和端口号 server_thread = threading.Thread(target=server.serve_forever) # 建立线程对象 server_thread.daemon = True # 守护线程 server_thread.start() # 开启线程,在线程中开启TCP服务器 print("Server loop running in thread:", server_thread.name) # 模拟三个socket客户端链接TCP服务器 client(ip, port, "Hello World 1") client(ip, port, "Hello World 2") client(ip, port, "Hello World 3") server.shutdown() # 打印内容以下: 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
参考文档:https://docs.python.org/3/library/socketserver.html?highlight=socketserver#module-socketserver