socketserver(在Python2.*中的是SocketServer模塊)是標准庫中一個高級別的模塊。用於簡化網絡客戶與服務器的實現(在前面使用socket的過程中,我們先設置了socket的類型,然后依次調用bind(),listen(),accept(),最后使用while循環來讓服務器不斷的接受請求。而這些步驟可以通過SocketServer包來簡化。)。模塊中,已經實現了一些可供使用的類。
我們將再次實現之前的那個基本TCP的例子。你會注意到新實現與之前有很多相似之處,但你也要注意到,現在很多繁雜的事情已經被封裝好了,你不用再去關心那個樣板代碼了。例子給出的是一個最簡單的同步服務器。
為了要隱藏實現的細節。我們現在寫程序時會使用類,這是與之前代碼的另一個不同。用面向對象的方法可以幫助我們更好的組織數據與邏輯功能。你也會注意到,我們的程序現在是“事件驅動”了。這就意味着,只有在事件出現的時候,程序才有“反應”。
在之前的服務循環中,我們阻塞等待請求,有請求來的時候就處理請求,然后再回去繼續等待。現在的服務循環中,就不用在服務器里寫代碼了,改成定義一個處理器,服務器在收到進來的請求的時候,可以調用你的處理函數。
類 描述
BaseServer 包含服務器的核心功能與混合(mix-in)類的鈎子功能。這個類用於派生,不要直接生成這個類的類對象,可以考慮使用 TCPServer 和UDPServer。
TCPServer/UDPServer 基本的網絡同步 TCP/UDP 服務器
UnixStreamServer/ 基本的基於文件同步 TCP/UDP 服務器
UnixDatagramServer
ForkingMixIn/ 實現了核心的進程化或線程化的功能,用於與服務器類進行混合(mix-in),以提供一些異步特性。
ThreadingMixIn 不要直接生成這個類的對象
ForkingTCPServer/ ForkingMixIn 和 TCPServer/UDPServer 的組合
ForkingUDPServer
ThreadingTCPServer/ ThreadingMixIn 和 TCPServer/UDPServer 的組合
ThreadingUDPServer
BaseRequestHandler 包含處理服務請求的核心功能。只用於派生新的類,不要直接生成這個類的對象,可以考慮使用 StreamRequestHandler 或DatagramRequestHandler
StreamRequestHandler/ TCP/UDP 服務器的請求處理類的一個實現
DatagramRequestHandler
創建一個socketserverTCP服務器
- from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH) #可以通過as起別名
- from time import ctime
- HOST = ''
- PORT = 1234
- ADDR = (HOST, PORT)
- class MyRequestHandler(SRH):
- def handle(self):
- print ('已經連接:', self.client_address)
- self.wfile.write(('[%s] %s' % (ctime(), self.rfile.readline().decode("UTF-8"))).encode("UTF-8"))
- tcpServ = TCP(ADDR, MyRequestHandler)
- print ('等待新的連接。。。。')
- tcpServ.serve_forever()
我們從socketserver的StreamRequestHandler類中派生出一個子類,並重寫handle()函數。在BaseRequest 類中,這個函數什么也不做。在有客戶消息進來的時候,handle()函數就會被調用。StreamRequestHandler 類支持像操作文件對象那樣操作輸入輸出套接字。我們可以用readline()函數得到客戶消息,用write()函數把字符串發給客戶。
創建一個socketserverTCP客戶端
- #coding=UTF-8
- from socket import *
- import sys
- reload (sys)
- sys.setdefaultencoding('utf8')
- HOST = '192.168.1.27'
- PORT = 1234
- BUFSIZE = 1024
- ADDR = (HOST, PORT)
- while True:
- tcpCliSock = socket(AF_INET, SOCK_STREAM)
- tcpCliSock.connect(ADDR)
- data = raw_input('>')
- if not data:
- break
- tcpCliSock.send('%s\r\n' % data.encode("UTF-8"))
- data = tcpCliSock.recv(BUFSIZE).decode("UTF-8")
- if not data:
- break
- print (data.strip())
- tcpCliSock.close()
使用socketserver處理多鏈接
上面的例子一次只能連接一個客戶機並出力它的請求,如果要處理多連接問題,那么有三種主要的方法能實現這個目的:分叉(forking)、線程(threading)以及異步I/O(asynchronous I/O)。通過對socketserver服務器使用混入類(mix-in class),派生進程和線程很容易處理。即使要自己實現它們,這些方法也很容易使用。它們確實有缺點:分叉占據資源,並且如果有太多的客戶端時分叉不能很好分叉(盡管如此,對於合理數量的客戶端,分叉在現代的UNIX或者Linux系統中是很高效的,如果有一個多CPU系統,那系統效率會更高);線程處理能導致同步問題。使用socketserver框架創建分叉或者線程服務器非常簡單:
分叉服務器:
- from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH,ForkingMixIn as FMI) #變動位置
- from time import ctime
- HOST = ''
- PORT = 1234
- ADDR = (HOST, PORT)
- class Server(FMI, TCP): #變動位置
- pass
- class MyRequestHandler(SRH):
- def handle(self):
- print ('已經連接:', self.client_address)
- self.wfile.write(('[%s] %s' % (ctime(), self.rfile.readline().decode("UTF-8"))).encode("UTF-8"))
- tcpServ = Server(ADDR, MyRequestHandler) #變動位置
- print ('等待新的連接。。。。')
- tcpServ.serve_forever()
多線程SocketServer服務器:
- from socketserver import (TCPServer as TCP, StreamRequestHandler as SRH,ThreadingMixIn as TMI) #變動位置
- from time import ctime
- HOST = ''
- PORT = 1234
- ADDR = (HOST, PORT)
- class Server(TMI, TCP): #變動位置
- pass
- class MyRequestHandler(SRH):
- def handle(self):
- print ('已經連接:', self.client_address)
- self.wfile.write(('[%s] %s' % (ctime(), self.rfile.readline().decode("UTF-8"))).encode("UTF-8"))
- tcpServ = Server(ADDR, MyRequestHandler) #變動位置
- print ('等待新的連接。。。。')
- tcpServ.serve_forever()