socketserver --- 用于網(wǎng)絡(luò)服務(wù)器的框架?

源代碼: Lib/socketserver.py


socketserver 模塊簡(jiǎn)化了編寫(xiě)網(wǎng)絡(luò)服務(wù)器的任務(wù)。

該模塊具有四個(gè)基礎(chǔ)實(shí)體服務(wù)器類(lèi):

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)?

該類(lèi)使用互聯(lián)網(wǎng) TCP 協(xié)議,它可以提供客戶(hù)端與服務(wù)器之間的連續(xù)數(shù)據(jù)流。 如果 bind_and_activate 為真值,該類(lèi)的構(gòu)造器會(huì)自動(dòng)嘗試發(fā)起調(diào)用 server_bind()server_activate()。 其他形參會(huì)被傳遞給 BaseServer 基類(lèi)。

class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)?

該類(lèi)使用數(shù)據(jù)包,即一系列離散的信息分包,它們可能會(huì)無(wú)序地到達(dá)或在傳輸中丟失。 該類(lèi)的形參與 TCPServer 的相同。

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)?
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True)?

這兩個(gè)更常用的類(lèi)與 TCP 和 UDP 類(lèi)相似,但使用 Unix 域套接字;它們?cè)诜?Unix 系統(tǒng)平臺(tái)上不可用。 它們的形參與 TCPServer 的相同。

這四個(gè)類(lèi)會(huì) 同步地 處理請(qǐng)求;每個(gè)請(qǐng)求必須完成才能開(kāi)始下一個(gè)請(qǐng)求。 這就不適用于每個(gè)請(qǐng)求要耗費(fèi)很長(zhǎng)時(shí)間來(lái)完成的情況,或者因?yàn)樗枰罅康挠?jì)算,又或者它返回了大量的數(shù)據(jù)而客戶(hù)端處理起來(lái)很緩慢。 解決方案是創(chuàng)建單獨(dú)的進(jìn)程或線程來(lái)處理每個(gè)請(qǐng)求;ForkingMixInThreadingMixIn 混合類(lèi)可以被用于支持異步行為。

創(chuàng)建一個(gè)服務(wù)器需要分幾個(gè)步驟進(jìn)行。 首先,你必須通過(guò)子類(lèi)化 BaseRequestHandler 類(lèi)并重載其 handle() 方法來(lái)創(chuàng)建一個(gè)請(qǐng)求處理句柄類(lèi);這個(gè)方法將處理傳入的請(qǐng)求。 其次,你必須實(shí)例化某個(gè)服務(wù)器類(lèi),將服務(wù)器地址和請(qǐng)求處理句柄類(lèi)傳給它。 建議在 with 語(yǔ)句中使用該服務(wù)器。 然后再調(diào)用服務(wù)器對(duì)象的 handle_request()serve_forever() 方法來(lái)處理一個(gè)或多個(gè)請(qǐng)求。 最后,調(diào)用 server_close() 來(lái)關(guān)閉套接字(除非你使用了 with 語(yǔ)句)。

當(dāng)從 ThreadingMixIn 繼承線程連接行為時(shí),你應(yīng)當(dāng)顯式地聲明你希望在突然關(guān)機(jī)時(shí)你的線程采取何種行為。 ThreadingMixIn 類(lèi)定義了一個(gè)屬性 daemon_threads,它指明服務(wù)器是否應(yīng)當(dāng)?shù)却€程終止。 如果你希望線程能自主行動(dòng)你應(yīng)當(dāng)顯式地設(shè)置這個(gè)旗標(biāo);默認(rèn)值為 False,表示 Python 將不會(huì)在 ThreadingMixIn 所創(chuàng)建的所有線程都退出之前退出。

服務(wù)器類(lèi)具有同樣的外部方法和屬性,無(wú)論它們使用哪種網(wǎng)絡(luò)協(xié)議。

服務(wù)器創(chuàng)建的說(shuō)明?

在繼承圖中有五個(gè)類(lèi),其中四個(gè)代表四種類(lèi)型的同步服務(wù)器:

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+

請(qǐng)注意 UnixDatagramServer 派生自 UDPServer,而不是 UnixStreamServer --- IP 和 Unix 流服務(wù)器的唯一區(qū)別是地址族,它會(huì)在兩種 Unix 服務(wù)器類(lèi)中簡(jiǎn)單地重復(fù)。

class socketserver.ForkingMixIn?
class socketserver.ThreadingMixIn?

每種服務(wù)器類(lèi)型的分叉和線程版本都可以使用這些混合類(lèi)來(lái)創(chuàng)建。 例如,ThreadingUDPServer 的創(chuàng)建方式如下:

class ThreadingUDPServer(ThreadingMixIn, UDPServer):
    pass

混合類(lèi)先出現(xiàn),因?yàn)樗剌d了 UDPServer 中定義的一個(gè)方法。 設(shè)置各種屬性也會(huì)改變下層服務(wù)器機(jī)制的行為。

ForkingMixIn 和下文提及的分叉類(lèi)僅在支持 fork() 的 POSIX 系統(tǒng)平臺(tái)上可用。

socketserver.ForkingMixIn.server_close() 會(huì)等待直到所有子進(jìn)程完成,除非 socketserver.ForkingMixIn.block_on_close 屬性為假值。

socketserver.ThreadingMixIn.server_close() 會(huì)等待直到所有非守護(hù)類(lèi)線程完成,除非 socketserver.ThreadingMixIn.block_on_close 屬性為假值。 請(qǐng)將 ThreadingMixIn.daemon_threads 設(shè)為 True 來(lái)使用守護(hù)類(lèi)線程以便不等待線完成。

在 3.7 版更改: socketserver.ForkingMixIn.server_close()socketserver.ThreadingMixIn.server_close() 現(xiàn)在會(huì)等待直到所有子進(jìn)程和非守護(hù)類(lèi)線程完成。 請(qǐng)新增一個(gè) socketserver.ForkingMixIn.block_on_close 類(lèi)屬性來(lái)選擇 3.7 版之前的行為。

class socketserver.ForkingTCPServer?
class socketserver.ForkingUDPServer?
class socketserver.ThreadingTCPServer?
class socketserver.ThreadingUDPServer?

這些類(lèi)都是使用混合類(lèi)來(lái)預(yù)定義的。

要實(shí)現(xiàn)一個(gè)服務(wù),你必須從 BaseRequestHandler 派生一個(gè)類(lèi)并重定義其 handle() 方法。 然后你可以通過(guò)組合某種服務(wù)器類(lèi)型與你的請(qǐng)求處理句柄類(lèi)來(lái)運(yùn)行各種版本的服務(wù)。 請(qǐng)求處理句柄類(lèi)對(duì)于數(shù)據(jù)報(bào)和流服務(wù)必須是不相同的。 這可以通過(guò)使用處理句柄子類(lèi) StreamRequestHandlerDatagramRequestHandler 來(lái)隱藏。

當(dāng)然,你仍然需要?jiǎng)狱c(diǎn)腦筋! 舉例來(lái)說(shuō),如果服務(wù)包含可能被不同請(qǐng)求所修改的內(nèi)存狀態(tài)則使用分叉服務(wù)器是沒(méi)有意義的,因?yàn)樵谧舆M(jìn)程中的修改將永遠(yuǎn)不會(huì)觸及保存在父進(jìn)程中的初始狀態(tài)并傳遞到各個(gè)子進(jìn)程。 在這種情況下,你可以使用線程服務(wù)器,但你可能必須使用鎖來(lái)保護(hù)共享數(shù)據(jù)的一致性。

另一方面,如果你是在編寫(xiě)一個(gè)所有數(shù)據(jù)保存在外部(例如文件系統(tǒng))的 HTTP 服務(wù)器,同步類(lèi)實(shí)際上將在正在處理某個(gè)請(qǐng)求的時(shí)候“失聰” -- 如果某個(gè)客戶(hù)端在接收它所請(qǐng)求的所有數(shù)據(jù)時(shí)很緩慢這可能會(huì)是非常長(zhǎng)的時(shí)間。 這時(shí)線程或分叉服務(wù)器會(huì)更為適用。

在某些情況下,合適的做法是同步地處理請(qǐng)求的一部分,但根據(jù)請(qǐng)求數(shù)據(jù)在分叉的子進(jìn)程中完成處理。 這可以通過(guò)使用一個(gè)同步服務(wù)器并在請(qǐng)求處理句柄類(lèi) handle() 中進(jìn)行顯式分叉來(lái)實(shí)現(xiàn)。

還有一種可以在既不支持線程也不支持 fork() 的環(huán)境(或者對(duì)于本服務(wù)來(lái)說(shuō)這兩者開(kāi)銷(xiāo)過(guò)大或是不適用)中處理多個(gè)同時(shí)請(qǐng)求的方式是維護(hù)一個(gè)顯式的部分完成的請(qǐng)求表并使用 selectors 來(lái)決定接下來(lái)要處理哪個(gè)請(qǐng)求(或者是否要處理一個(gè)新傳入的請(qǐng)求)。 這對(duì)于流服務(wù)來(lái)說(shuō)特別重要,因?yàn)槊總€(gè)客戶(hù)端可能會(huì)連接很長(zhǎng)的時(shí)間(如果不能使用線程或子進(jìn)程)。 請(qǐng)參閱 asyncore 來(lái)了解另一種管理方式。

Server 對(duì)象?

class socketserver.BaseServer(server_address, RequestHandlerClass)?

這是本模塊中所有 Server 對(duì)象的超類(lèi)。 它定義了下文給出的接口,但沒(méi)有實(shí)現(xiàn)大部分的方法,它們應(yīng)在子類(lèi)中實(shí)現(xiàn)。 兩個(gè)形參存儲(chǔ)在對(duì)應(yīng)的 server_addressRequestHandlerClass 屬性中。

fileno()?

返回服務(wù)器正在監(jiān)聽(tīng)的套接字的以整數(shù)表示的文件描述符。 此函數(shù)最常被傳遞給 selectors,以允許在同一進(jìn)程中監(jiān)控多個(gè)服務(wù)器。

handle_request()?

處理單個(gè)請(qǐng)求。 此函數(shù)會(huì)依次調(diào)用下列方法: get_request(), verify_request()process_request()。 如果用戶(hù)提供的處理句柄類(lèi)的 handle() 方法引發(fā)了異常,則將調(diào)用服務(wù)器的 handle_error() 方法。 如果在 timeout 秒內(nèi)未接收到請(qǐng)求,將會(huì)調(diào)用 handle_timeout() 并將返回 handle_request()。

serve_forever(poll_interval=0.5)?

對(duì)請(qǐng)求進(jìn)行處理直至收到顯式的 shutdown() 請(qǐng)求。 每隔 poll_interval 秒對(duì) shutdown 進(jìn)行輪詢(xún)。 忽略 timeout 屬性。 它還會(huì)調(diào)用 service_actions(),這可被子類(lèi)或混合類(lèi)用來(lái)提供某個(gè)給定服務(wù)的專(zhuān)屬操作。 例如,ForkingMixIn 類(lèi)使用 service_actions() 來(lái)清理僵尸子進(jìn)程。

在 3.3 版更改: service_actions 調(diào)用添加到 serve_forever 方法。

service_actions()?

此方法會(huì)在 the serve_forever() 循環(huán)中被調(diào)用。 此方法可被子類(lèi)或混合類(lèi)所重載以執(zhí)行某個(gè)給定服務(wù)的專(zhuān)屬操作,例如清理操作。

3.3 新版功能.

shutdown()?

通知 serve_forever() 循環(huán)停止并等待它完成。 shutdown() 必須在 serve_forever() 運(yùn)行于不同線程時(shí)被調(diào)用否則它將發(fā)生死鎖。

server_close()?

清理服務(wù)器。 此方法可被重載。

address_family?

服務(wù)器套接字所屬的協(xié)議族。 常見(jiàn)的例子有 socket.AF_INETsocket.AF_UNIX。

RequestHandlerClass?

用戶(hù)提供的請(qǐng)求處理句柄類(lèi);將為每個(gè)請(qǐng)求創(chuàng)建該類(lèi)的實(shí)例。

server_address?

服務(wù)器所監(jiān)聽(tīng)的地址。 地址的格式因具體協(xié)議族而不同;請(qǐng)參閱 socket 模塊的文檔了解詳情。 對(duì)于互聯(lián)網(wǎng)協(xié)議,這將是一個(gè)元組,其中包含一個(gè)表示地址的字符串,和一個(gè)表示端口號(hào)的整數(shù),例如: ('127.0.0.1', 80)。

socket?

將由服務(wù)器用于監(jiān)聽(tīng)入站請(qǐng)求的套接字對(duì)象。

服務(wù)器類(lèi)支持下列類(lèi)變量:

allow_reuse_address?

服務(wù)器是否要允許地址的重用。 默認(rèn)值為 False,并可在子類(lèi)中設(shè)置以改變策略。

request_queue_size?

請(qǐng)求隊(duì)列的長(zhǎng)度。 如果處理單個(gè)請(qǐng)求要花費(fèi)很長(zhǎng)的時(shí)間,則當(dāng)服務(wù)器正忙時(shí)到達(dá)的任何請(qǐng)求都會(huì)被加入隊(duì)列,最多加入 request_queue_size 個(gè)請(qǐng)求。 一旦隊(duì)列被加滿(mǎn),來(lái)自客戶(hù)端的更多請(qǐng)求將收到 "Connection denied" 錯(cuò)誤。 默認(rèn)值為 5,但可在子類(lèi)中重載。

socket_type?

服務(wù)器使用的套接字類(lèi)型;常見(jiàn)的有 socket.SOCK_STREAMsocket.SOCK_DGRAM 這兩個(gè)值。

timeout?

超時(shí)限制,以秒數(shù)表示,或者如果不限制超時(shí)則為 None。 如果在超時(shí)限制期間沒(méi)有收到 handle_request(),則會(huì)調(diào)用 handle_timeout() 方法。

有多個(gè)服務(wù)器方法可被服務(wù)器基類(lèi)的子類(lèi)例如 TCPServer 所重載;這些方法對(duì)服務(wù)器對(duì)象的外部用戶(hù)來(lái)說(shuō)并無(wú)用處。

finish_request(request, client_address)?

通過(guò)實(shí)例化 RequestHandlerClass 并調(diào)用其 handle() 方法來(lái)實(shí)際處理請(qǐng)求。

get_request()?

必須接受來(lái)自套接字的請(qǐng)求,并返回一個(gè) 2 元組,其中包含用來(lái)與客戶(hù)端通信的 new 套接字對(duì)象,以及客戶(hù)端的地址。

handle_error(request, client_address)?

此函數(shù)會(huì)在 RequestHandlerClass 實(shí)例的 handle() 方法引發(fā)異常時(shí)被調(diào)用。 默認(rèn)行為是將回溯信息打印到標(biāo)準(zhǔn)錯(cuò)誤并繼續(xù)處理其他請(qǐng)求。

在 3.6 版更改: 現(xiàn)在只針對(duì)派生自 Exception 類(lèi)的異常調(diào)用此方法。

handle_timeout()?

此函數(shù)會(huì)在 timeout 屬性被設(shè)為 None 以外的值并且在超出時(shí)限之后仍未收到請(qǐng)求時(shí)被調(diào)用。 分叉服務(wù)器的默認(rèn)行為是收集任何已退出的子進(jìn)程狀態(tài),而在線程服務(wù)器中此方法則不做任何操作。

process_request(request, client_address)?

調(diào)用 finish_request() 來(lái)創(chuàng)建 RequestHandlerClass 的實(shí)例。 如果需要,此函數(shù)可創(chuàng)建一個(gè)新的進(jìn)程或線程來(lái)處理請(qǐng)求;ForkingMixInThreadingMixIn 類(lèi)能完成此任務(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,請(qǐng)求將被處理。 而如果值為 False,請(qǐng)求將被拒絕。 此函數(shù)可被重載以實(shí)現(xiàn)服務(wù)器的訪問(wèn)控制。 默認(rèn)實(shí)現(xiàn)總是返回 True。

在 3.6 版更改: 添加了對(duì) context manager 協(xié)議的支持。 退出上下文管理器與調(diào)用 server_close() 等效。

請(qǐng)求處理句柄對(duì)象?

class socketserver.BaseRequestHandler?

這是所有請(qǐng)求處理句柄對(duì)象的超類(lèi)。 它定義了下文列出的接口。 一個(gè)實(shí)體請(qǐng)求處理句柄子類(lèi)必須定義新的 handle() 方法,并可重載任何其他方法。 對(duì)于每個(gè)請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的子類(lèi)的實(shí)例。

setup()?

會(huì)在 handle() 方法之前被調(diào)用以執(zhí)行任何必要的初始化操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。

handle()?

此函數(shù)必須執(zhí)行為請(qǐng)求提供服務(wù)所需的全部操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。 它有幾個(gè)可用的實(shí)例屬性;請(qǐng)求為 self.request;客戶(hù)端地址為 self.client_address;服務(wù)器實(shí)例為 self.server,如果它需要訪問(wèn)特定服務(wù)器信息的話(huà)。

針對(duì)數(shù)據(jù)報(bào)或流服務(wù)的 self.request 類(lèi)型是不同的。 對(duì)于流服務(wù),self.request 是一個(gè)套接字對(duì)象;對(duì)于數(shù)據(jù)報(bào)服務(wù),self.request 是一對(duì)字符串與套接字。

finish()?

handle() 方法之后調(diào)用以執(zhí)行任何需要的清理操作。 默認(rèn)實(shí)現(xiàn)不執(zhí)行任何操作。 如果 setup() 引發(fā)了異常,此函數(shù)將不會(huì)被調(diào)用。

class socketserver.StreamRequestHandler?
class socketserver.DatagramRequestHandler?

BaseRequestHandler 子類(lèi)重載了 setup()finish() 方法,并提供了 self.rfileself.wfile 屬性。 self.rfileself.wfile 屬性可以被分別讀取或?qū)懭?,以獲取請(qǐng)求數(shù)據(jù)或?qū)?shù)據(jù)返回給客戶(hù)端。

這兩個(gè)類(lèi)的 rfile 屬性都支持 io.BufferedIOBase 可讀接口,并且 DatagramRequestHandler.wfile 還支持 io.BufferedIOBase 可寫(xiě)接口。

在 3.6 版更改: StreamRequestHandler.wfile 也支持 io.BufferedIOBase 可寫(xiě)接口。

例子?

socketserver.TCPServer 示例?

以下是服務(wù)端:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # Activate the server; this will keep running until you
        # interrupt the program with Ctrl-C
        server.serve_forever()

一個(gè)使用流(通過(guò)提供標(biāo)準(zhǔn)文件接口來(lái)簡(jiǎn)化通信的文件類(lèi)對(duì)象)的替代請(qǐng)求處理句柄類(lèi):

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() 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())

區(qū)別在于第二個(gè)處理句柄的 readline() 調(diào)用將多次調(diào)用 recv() 直至遇到一個(gè)換行符,而第一個(gè)處理句柄的單次 recv() 調(diào)用只是返回在一次 sendall() 調(diào)用中由客戶(hù)端發(fā)送的內(nèi)容。

以下是客戶(hù)端:

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".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 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))

這個(gè)示例程序的輸出應(yīng)該是像這樣的:

服務(wù)器:

$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'

客戶(hù)端:

$ python TCPClient.py hello world with TCP
Sent:     hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent:     python is nice
Received: PYTHON IS NICE

socketserver.UDPServer 示例?

以下是服務(wù)端:

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()

以下是客戶(hù)端:

import socket
import sys

HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])

# 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))

這個(gè)示例程序的輸出應(yīng)該是與 TCP 服務(wù)器示例相一致的。

異步混合類(lèi)?

要構(gòu)建異步處理句柄,請(qǐng)使用 ThreadingMixInForkingMixIn 類(lèi)。

ThreadingMixIn 類(lèi)的示例:

import socket
import threading
import socketserver

class 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):
    pass

def 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 port
    HOST, PORT = "localhost", 0

    server = 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 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()

這個(gè)示例程序的輸出應(yīng)該是像這樣的:

$ 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

ForkingMixIn 類(lèi)的使用方式是相同的,區(qū)別在于服務(wù)器將為每個(gè)請(qǐng)求產(chǎn)生一個(gè)新的進(jìn)程。 僅在支持 fork() 的 POSIX 系統(tǒng)平臺(tái)上可用。