新聞中心
socketserver —- 用于網(wǎng)絡(luò)服務(wù)器的框架
源代碼: Lib/socketserver.py

socketserver 模塊簡化了編寫網(wǎng)絡(luò)服務(wù)器的任務(wù)。
Availability: not Emscripten, not WASI.
This module does not work or is not available on WebAssembly platforms wasm32-emscripten and wasm32-wasi. See WebAssembly platforms for more information.
該模塊具有四個(gè)基礎(chǔ)實(shí)體服務(wù)器類:
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
該類使用互聯(lián)網(wǎng) TCP 協(xié)議,它可以提供客戶端與服務(wù)器之間的連續(xù)數(shù)據(jù)流。 如果 bind_and_activate 為真值,該類的構(gòu)造器會自動(dòng)嘗試發(fā)起調(diào)用 server_bind() 和 server_activate()。 其他形參會被傳遞給 BaseServer 基類。
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
該類使用數(shù)據(jù)包,即一系列離散的信息分包,它們可能會無序地到達(dá)或在傳輸中丟失。 該類的形參與 TCPServer 的相同。
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)
這兩個(gè)更常用的類與 TCP 和 UDP 類相似,但使用 Unix 域套接字;它們在非 Unix 系統(tǒng)平臺上不可用。 它們的形參與 TCPServer 的相同。
這四個(gè)類會 同步地 處理請求;每個(gè)請求必須完成才能開始下一個(gè)請求。 這就不適用于每個(gè)請求要耗費(fèi)很長時(shí)間來完成的情況,或者因?yàn)樗枰罅康挠?jì)算,又或者它返回了大量的數(shù)據(jù)而客戶端處理起來很緩慢。 解決方案是創(chuàng)建單獨(dú)的進(jìn)程或線程來處理每個(gè)請求;ForkingMixIn 和 ThreadingMixIn 混合類可以被用于支持異步行為。
創(chuàng)建一個(gè)服務(wù)器需要分幾個(gè)步驟進(jìn)行。 首先,你必須通過子類化 BaseRequestHandler 類并重載其 handle() 方法來創(chuàng)建一個(gè)請求處理句柄類;這個(gè)方法將處理傳入的請求。 其次,你必須實(shí)例化某個(gè)服務(wù)器類,將服務(wù)器地址和請求處理句柄類傳給它。 建議在 with 語句中使用該服務(wù)器。 然后再調(diào)用服務(wù)器對象的 handle_request() 或 serve_forever() 方法來處理一個(gè)或多個(gè)請求。 最后,調(diào)用 server_close() 來關(guān)閉套接字(除非你使用了 with 語句)。
當(dāng)從 ThreadingMixIn 繼承線程連接行為時(shí),你應(yīng)當(dāng)顯式地聲明你希望在突然關(guān)機(jī)時(shí)你的線程采取何種行為。 ThreadingMixIn 類定義了一個(gè)屬性 daemon_threads,它指明服務(wù)器是否應(yīng)當(dāng)?shù)却€程終止。 如果你希望線程能自主行動(dòng)你應(yīng)當(dāng)顯式地設(shè)置這個(gè)旗標(biāo);默認(rèn)值為 False,表示 python 將不會在 ThreadingMixIn 所創(chuàng)建的所有線程都退出之前退出。
服務(wù)器類具有同樣的外部方法和屬性,無論它們使用哪種網(wǎng)絡(luò)協(xié)議。
服務(wù)器創(chuàng)建的說明
在繼承圖中有五個(gè)類,其中四個(gè)代表四種類型的同步服務(wù)器:
+------------+| BaseServer |+------------+|v+-----------+ +------------------+| TCPServer |------->| UnixStreamServer |+-----------+ +------------------+|v+-----------+ +--------------------+| UDPServer |------->| UnixDatagramServer |+-----------+ +--------------------+
請注意 UnixDatagramServer 派生自 UDPServer,而不是 UnixStreamServer —- IP 和 Unix 流服務(wù)器的唯一區(qū)別是地址族,它會在兩種 Unix 服務(wù)器類中簡單地重復(fù)。
class socketserver.ForkingMixIn
class socketserver.ThreadingMixIn
每種服務(wù)器類型的分叉和線程版本都可以使用這些混合類來創(chuàng)建。 例如,ThreadingUDPServer 的創(chuàng)建方式如下:
class ThreadingUDPServer(ThreadingMixIn, UDPServer):pass
混合類先出現(xiàn),因?yàn)樗剌d了 UDPServer 中定義的一個(gè)方法。 設(shè)置各種屬性也會改變下層服務(wù)器機(jī)制的行為。
ForkingMixIn 和下文提及的分叉類僅在支持 fork() 的 POSIX 系統(tǒng)平臺上可用。
socketserver.ForkingMixIn.server_close() 會等待直到所有子進(jìn)程完成,除非 socketserver.ForkingMixIn.block_on_close 屬性為假值。
socketserver.ThreadingMixIn.server_close() 會等待直到所有非守護(hù)類線程完成,除非 socketserver.ThreadingMixIn.block_on_close 屬性為假值。 請將 ThreadingMixIn.daemon_threads 設(shè)為 True 來使用守護(hù)類線程以便不等待線完成。
在 3.7 版更改: socketserver.ForkingMixIn.server_close() 和 socketserver.ThreadingMixIn.server_close() 現(xiàn)在會等待直到所有子進(jìn)程和非守護(hù)類線程完成。 請新增一個(gè) socketserver.ForkingMixIn.block_on_close 類屬性來選擇 3.7 版之前的行為。
class socketserver.ForkingTCPServer
class socketserver.ForkingUDPServer
class socketserver.ThreadingTCPServer
class socketserver.ThreadingUDPServer
這些類都是使用混合類來預(yù)定義的。
要實(shí)現(xiàn)一個(gè)服務(wù),你必須從 BaseRequestHandler 派生一個(gè)類并重定義其 handle() 方法。 然后你可以通過組合某種服務(wù)器類型與你的請求處理句柄類來運(yùn)行各種版本的服務(wù)。 請求處理句柄類對于數(shù)據(jù)報(bào)和流服務(wù)必須是不相同的。 這可以通過使用處理句柄子類 StreamRequestHandler 或 DatagramRequestHandler 來隱藏。
當(dāng)然,你仍然需要?jiǎng)狱c(diǎn)腦筋! 舉例來說,如果服務(wù)包含可能被不同請求所修改的內(nèi)存狀態(tài)則使用分叉服務(wù)器是沒有意義的,因?yàn)樵谧舆M(jìn)程中的修改將永遠(yuǎn)不會觸及保存在父進(jìn)程中的初始狀態(tài)并傳遞到各個(gè)子進(jìn)程。 在這種情況下,你可以使用線程服務(wù)器,但你可能必須使用鎖來保護(hù)共享數(shù)據(jù)的一致性。
另一方面,如果你是在編寫一個(gè)所有數(shù)據(jù)保存在外部(例如文件系統(tǒng))的 HTTP 服務(wù)器,同步類實(shí)際上將在正在處理某個(gè)請求的時(shí)候“失聰” — 如果某個(gè)客戶端在接收它所請求的所有數(shù)據(jù)時(shí)很緩慢這可能會是非常長的時(shí)間。 這時(shí)線程或分叉服務(wù)器會更為適用。
在某些情況下,合適的做法是同步地處理請求的一部分,但根據(jù)請求數(shù)據(jù)在分叉的子進(jìn)程中完成處理。 這可以通過使用一個(gè)同步服務(wù)器并在請求處理句柄類 handle() 中進(jìn)行顯式分叉來實(shí)現(xiàn)。
還有一種可以在既不支持線程也不支持 fork() 的環(huán)境(或者對于本服務(wù)來說這兩者開銷過大或是不適用)中處理多個(gè)同時(shí)請求的方式是維護(hù)一個(gè)顯式的部分完成的請求表并使用 selectors 來決定接下來要處理哪個(gè)請求(或者是否要處理一個(gè)新傳入的請求)。 這對于流服務(wù)來說特別重要,因?yàn)槊總€(gè)客戶端可能會連接很長的時(shí)間(如果不能使用線程或子進(jìn)程)。 請參閱 asyncore 來了解另一種管理方式。
Server 對象
class socketserver.BaseServer(server_address, RequestHandlerClass)
這是本模塊中所有 Server 對象的超類。 它定義了下文給出的接口,但沒有實(shí)現(xiàn)大部分的方法,它們應(yīng)在子類中實(shí)現(xiàn)。 兩個(gè)形參存儲在對應(yīng)的 server_address 和 RequestHandlerClass 屬性中。
-
fileno()
返回服務(wù)器正在監(jiān)聽的套接字的以整數(shù)表示的文件描述符。 此函數(shù)最常被傳遞給 selectors,以允許在同一進(jìn)程中監(jiān)控多個(gè)服務(wù)器。
-
handle_request()
處理單個(gè)請求。 此函數(shù)會依次調(diào)用下列方法: get_request(), verify_request() 和 process_request()。 如果用戶提供的處理句柄類的 handle() 方法引發(fā)了異常,則將調(diào)用服務(wù)器的 handle_error() 方法。 如果在 timeout 秒內(nèi)未接收到請求,將會調(diào)用 handle_timeout() 并將返回 handle_request()。
-
serve_forever(poll_interval=0.5)
對請求進(jìn)行處理直至收到顯式的 shutdown() 請求。 每隔 poll_interval 秒對 shutdown 進(jìn)行輪詢。 忽略 timeout 屬性。 它還會調(diào)用 service_actions(),這可被子類或混合類用來提供某個(gè)給定服務(wù)的專屬操作。 例如,F(xiàn)orkingMixIn 類使用 service_actions() 來清理僵尸子進(jìn)程。
在 3.3 版更改: 將
service_actions調(diào)用添加到serve_forever方法。 -
service_actions()
此方法會在 the serve_forever() 循環(huán)中被調(diào)用。 此方法可被子類或混合類所重載以執(zhí)行某個(gè)給定服務(wù)的專屬操作,例如清理操作。
3.3 新版功能.
-
shutdown()
通知 serve_forever() 循環(huán)停止并等待它完成。 shutdown() 必須在 serve_forever() 運(yùn)行于不同線程時(shí)被調(diào)用否則它將發(fā)生死鎖。
-
server_close()
清理服務(wù)器。 此方法可被重載。
-
address_family
服務(wù)器套接字所屬的協(xié)議族。 常見的例子有 socket.AF_INET 和 socket.AF_UNIX。
-
RequestHandlerClass
用戶提供的請求處理句柄類;將為每個(gè)請求創(chuàng)建該類的實(shí)例。
-
server_address
服務(wù)器所監(jiān)聽的地址。 地址的格式因具體協(xié)議族而不同;請參閱 socket 模塊的文檔了解詳情。 對于互聯(lián)網(wǎng)協(xié)議,這將是一個(gè)元組,其中包含一個(gè)表示地址的字符串,和一個(gè)表示端口號的整數(shù),例如:
('127.0.0.1', 80)。 -
socket
將由服務(wù)器用于監(jiān)聽入站請求的套接字對象。
服務(wù)器類支持下列類變量:
-
allow_reuse_address
服務(wù)器是否要允許地址的重用。 默認(rèn)值為 False,并可在子類中設(shè)置以改變策略。
-
request_queue_size
請求隊(duì)列的長度。 如果處理單個(gè)請求要花費(fèi)很長的時(shí)間,則當(dāng)服務(wù)器正忙時(shí)到達(dá)的任何請求都會被加入隊(duì)列,最多加入 request_queue_size 個(gè)請求。 一旦隊(duì)列被加滿,來自客戶端的更多請求將收到 “Connection denied” 錯(cuò)誤。 默認(rèn)值為 5,但可在子類中重載。
-
socket_type
服務(wù)器使用的套接字類型;常見的有 socket.SOCK_STREAM 和 socket.SOCK_DGRAM 這兩個(gè)值。
-
timeout
超時(shí)限制,以秒數(shù)表示,或者如果不限制超時(shí)則為 None。 如果在超時(shí)限制期間沒有收到 handle_request(),則會調(diào)用 handle_timeout() 方法。
有多個(gè)服務(wù)器方法可被服務(wù)器基類的子類例如 TCPServer 所重載;這些方法對服務(wù)器對象的外部用戶來說并無用處。
-
finish_request(request, client_address)
通過實(shí)例化 RequestHandlerClass 并調(diào)用其 handle() 方法來實(shí)際處理請求。
-
get_request()
必須接受來自套接字的請求,并返回一個(gè) 2 元組,其中包含用來與客戶端通信的 new 套接字對象,以及客戶端的地址。
-
handle_error(request, client_address)
此函數(shù)會在 RequestHandlerClass 實(shí)例的 handle() 方法引發(fā)異常時(shí)被調(diào)用。 默認(rèn)行為是將回溯信息打印到標(biāo)準(zhǔn)錯(cuò)誤并繼續(xù)處理其他請求。
在 3.6 版更改: 現(xiàn)在只針對派生自 Exception 類的異常調(diào)用此方法。
-
handle_timeout()
此函數(shù)會在 timeout 屬性被設(shè)為 None 以外的值并且在超出時(shí)限之后仍未收到請求時(shí)被調(diào)用。 分叉服務(wù)器的默認(rèn)行為是收集任何已退出的子進(jìn)程狀態(tài),而在線程服務(wù)器中此方法則不做任何操作。
-
process_request(request, client_address)
調(diào)用 finish_request() 來創(chuàng)建 RequestHandlerClass 的實(shí)例。 如果需要,此函數(shù)可創(chuàng)建一個(gè)新的進(jìn)程或線程來處理請求;ForkingMixIn 和 ThreadingMixIn 類能完成此任務(wù)。
-
server_activate()
由服務(wù)器的構(gòu)造器調(diào)用以激活服務(wù)器。 TCP 服務(wù)器的默認(rèn)行為只是在服務(wù)器的套接字上發(fā)起調(diào)用 listen()。 可以被重載。
-
server_bind()
由服務(wù)器的構(gòu)造器調(diào)用以將套接字綁定到所需的地址。 可以被重載。
-
verify_request(request, client_address)
必須返回一個(gè)布爾值;如果值為 True,請求將被處理。 而如果值為 False,請求將被拒絕。 此函數(shù)可被重載以實(shí)現(xiàn)服務(wù)器的訪問控制。 默認(rèn)實(shí)現(xiàn)總是返回 True。
在 3.6 版更改: 添加了對 context manager 協(xié)議的支持。 退出上下文管理器與調(diào)用 server_close() 等效。
請求處理句柄對象
class socketserver.BaseRequestHandler
這是所有請求處理句柄對象的超類。 它定義了下文列出的接口。 一個(gè)實(shí)體請求處理句柄子類必須定義新的 handle() 方法,并可重載任何其他方法。 對于每個(gè)請求都會創(chuàng)建一個(gè)新的子類的實(shí)例。
-
setup()
會在 handle() 方法之前被調(diào)用以執(zhí)行任何必要的初始化操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。
-
handle()
此函數(shù)必須執(zhí)行為請求提供服務(wù)所需的全部操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。 它有幾個(gè)可用的實(shí)例屬性;請求為
self.request;客戶端地址為self.client_address;服務(wù)器實(shí)例為self.server,如果它需要訪問特定服務(wù)器信息的話。針對數(shù)據(jù)報(bào)或流服務(wù)的
self.request類型是不同的。 對于流服務(wù),self.request是一個(gè)套接字對象;對于數(shù)據(jù)報(bào)服務(wù),self.request是一對字符串與套接字。 -
finish()
在 handle() 方法之后調(diào)用以執(zhí)行任何需要的清理操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。 如果 setup() 引發(fā)了異常,此函數(shù)將不會被調(diào)用。
class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler
BaseRequestHandler 子類重載了 setup() 和 finish() 方法,并提供了 self.rfile 和 self.wfile 屬性。 self.rfile 和 self.wfile 屬性可以被分別讀取或?qū)懭?,以獲取請求數(shù)據(jù)或?qū)?shù)據(jù)返回給客戶端。
這兩個(gè)類的 rfile 屬性都支持 io.BufferedIOBase 可讀接口,并且 DatagramRequestHandler.wfile 還支持 io.BufferedIOBase 可寫接口。
在 3.6 版更改: StreamRequestHandler.wfile 也支持 io.BufferedIOBase 可寫接口。
例子
socketserver.TCPServer 示例
以下是服務(wù)端:
import socketserverclass MyTCPHandler(socketserver.BaseRequestHandler):"""The request handler class for our server.It is instantiated once per connection to the server, and mustoverride the handle() method to implement communication to theclient."""def handle(self):# self.request is the TCP socket connected to the clientself.data = self.request.recv(1024).strip()print("{} wrote:".format(self.client_address[0]))print(self.data)# just send back the same data, but upper-casedself.request.sendall(self.data.upper())if __name__ == "__main__":HOST, PORT = "localhost", 9999# Create the server, binding to localhost on port 9999with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:# Activate the server; this will keep running until you# interrupt the program with Ctrl-Cserver.serve_forever()
一個(gè)使用流(通過提供標(biāo)準(zhǔn)文件接口來簡化通信的文件類對象)的替代請求處理句柄類:
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() callsself.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 clientself.wfile.write(self.data.upper())
區(qū)別在于第二個(gè)處理句柄的 readline() 調(diào)用將多次調(diào)用 recv() 直至遇到一個(gè)換行符,而第一個(gè)處理句柄的單次 recv() 調(diào)用只是返回在一次 sendall() 調(diào)用中由客戶端發(fā)送的內(nèi)容。
以下是客戶端:
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 datasock.connect((HOST, PORT))sock.sendall(bytes(data + "\n", "utf-8"))# Receive data from the server and shut downreceived = str(sock.recv(1024), "utf-8")print("Sent: {}".format(data))print("Received: {}".format(received))
這個(gè)示例程序的輸出應(yīng)該是像這樣的:
服務(wù)器:
$ 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 示例
以下是服務(wù)端:
import socketserverclass MyUDPHandler(socketserver.BaseRequestHandler):"""This class works similar to the TCP handler class, except thatself.request consists of a pair of data and client socket, and sincethere is no connection the client address must be given explicitlywhen 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", 9999with 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))
這個(gè)示例程序的輸出應(yīng)該是與 TCP 服務(wù)器示例相一致的。
異步混合類
要構(gòu)建異步處理句柄,請使用 ThreadingMixIn 和 ForkingMixIn 類。
ThreadingMixIn 類的示例:
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 portHOST, PORT = "localhost", 0server = 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 requestserver_thread = threading.Thread(target=server.serve_forever)# Exit the server thread when the main thread terminatesserver_thread.daemon = Trueserver_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()
這個(gè)示例程序的輸出應(yīng)該是像這樣的:
$ 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 類的使用方式是相同的,區(qū)別在于服務(wù)器將為每個(gè)請求產(chǎn)生一個(gè)新的進(jìn)程。 僅在支持 fork() 的 POSIX 系統(tǒng)平臺上可用。
本文題目:創(chuàng)新互聯(lián)Python教程:socketserver—-用于網(wǎng)絡(luò)服務(wù)器的框架
本文地址:http://www.5511xx.com/article/cdicgop.html


咨詢
建站咨詢
