Python的socket模塊源碼中的一些實(shí)現(xiàn)要點(diǎn)分析
BaseServer 和 BaseRequestHandler
Python為網(wǎng)絡(luò)編程提高了更高級(jí)的封裝。SocketServer.py 提供了不少網(wǎng)絡(luò)服務(wù)的類(lèi)。它們的設(shè)計(jì)很優(yōu)雅。Python把網(wǎng)絡(luò)服務(wù)抽象成兩個(gè)主要的類(lèi),一個(gè)是Server類(lèi),用于處理連接相關(guān)的網(wǎng)絡(luò)操作,另外一個(gè)則是RequestHandler類(lèi),用于處理數(shù)據(jù)相關(guān)的操作。并且提供兩個(gè)MixIn 類(lèi),用于擴(kuò)展 Server,實(shí)現(xiàn)多進(jìn)程或多線(xiàn)程。在構(gòu)建網(wǎng)絡(luò)服務(wù)的時(shí)候,Server 和 RequestHandler 并不是分開(kāi)的,RequestHandler的實(shí)例對(duì)象在Server 內(nèi)配合 Server工作。
改模塊的主要幾個(gè)Server關(guān)系如下:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
BaseServer 分析
BaseServer 通過(guò)__init__初始化,對(duì)外提供serve_forever和 handler_request方法。
init 初始化:
def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.__is_shut_down = threading.Event()
self.__shutdown_request = False
__init__源碼很簡(jiǎn)單。主要作用是創(chuàng)建server對(duì)象,并初始化server地址和處理請(qǐng)求的class。熟悉socket編程應(yīng)該很清楚,server_address是一個(gè)包含主機(jī)和端口的元組。
serve_forever
創(chuàng)建了server對(duì)象之后,就需要使用server對(duì)象開(kāi)啟一個(gè)無(wú)限循環(huán),下面來(lái)分析serve_forever的源碼。
def serve_forever(self, poll_interval=0.5):
self.__is_shut_down.clear()
try:
while not self.__shutdown_request:
r, w, e = _eintr_retry(select.select, [self], [], [],
poll_interval)
if self in r:
self._handle_request_noblock()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
serve_forever接受一個(gè)參數(shù)poll_interval,用于表示select輪詢(xún)的時(shí)間。然后進(jìn)入一個(gè)無(wú)限循環(huán),調(diào)用select方式進(jìn)行網(wǎng)絡(luò)IO的監(jiān)聽(tīng)。
如果select函數(shù)返回,表示有IO連接或數(shù)據(jù),那么將會(huì)調(diào)用_handle_request_noblock方法。
_handle_request_noblock
def _handle_request_noblock(self):
try:
request, client_address = self.get_request()
except socket.error:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
_handle_request_noblock方法即開(kāi)始處理一個(gè)請(qǐng)求,并且是非阻塞。該方法通過(guò)get_request方法獲取連接,具體的實(shí)現(xiàn)在其子類(lèi)。一旦得到了連接,調(diào)用verify_request方法驗(yàn)證請(qǐng)求。驗(yàn)證通過(guò),即調(diào)用process_request處理請(qǐng)求。如果中途出現(xiàn)錯(cuò)誤,則調(diào)用handle_error處理錯(cuò)誤,以及shutdown_request結(jié)束連接。
verify_request
def verify_request(self, request, client_address):
return True
該方法對(duì)request進(jìn)行驗(yàn)證,通常會(huì)被子類(lèi)重寫(xiě)。簡(jiǎn)單的返回True即可,然后進(jìn)入process_request方法處理請(qǐng)求。
process_request
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.shutdown_request(request)
process_request方法是mixin的入口,MixIn子類(lèi)通過(guò)重寫(xiě)該方法,進(jìn)行多線(xiàn)程或多進(jìn)程的配置。調(diào)用finish_request完成請(qǐng)求的處理,同時(shí)調(diào)用shutdown_request結(jié)束請(qǐng)求。
finish_request
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
finish_request方法將會(huì)處理完畢請(qǐng)求。創(chuàng)建requestHandler對(duì)象,并通過(guò)requestHandler做具體的處理。
BaseRequestHandler 分析
所有requestHandler都繼承BaseRequestHandler基類(lèi)。
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
該類(lèi)會(huì)處理每一個(gè)請(qǐng)求。初始化對(duì)象的時(shí)候,設(shè)置請(qǐng)求request對(duì)象。然后調(diào)用setup方法,子類(lèi)會(huì)重寫(xiě)該方法,用于處理socket連接。接下來(lái)的將是handler和finish方法。所有對(duì)請(qǐng)求的處理,都可以重寫(xiě)handler方法。
至此,整個(gè)Python提供的Server方式即介紹完畢??偨Y(jié)一下,構(gòu)建一個(gè)網(wǎng)絡(luò)服務(wù),需要一個(gè)BaseServer用于處理網(wǎng)絡(luò)IO,同時(shí)在內(nèi)部創(chuàng)建requestHandler對(duì)象,對(duì)所有具體的請(qǐng)求做處理。
BaseServer - BaseRequestHandler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
serve_forever():
select()
BaseServer._handle_request_noblock()
BaseServer.get_request() -> request, client_addres
BaseServer.verify_request()
BaseServer.process_request()
BaseServer.process_request()
BaseServer.finish_request()
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
BaseRequestHandler.setup()
BaseRequestHandler.handle()
BaseServer.shutdown_request()
BaseServer.close_request()
BaseServer.shutdown_request()
BaseServer.close_request()
BaseServer 和 BaseRequestHandler是網(wǎng)絡(luò)處理的兩個(gè)基類(lèi)。實(shí)際應(yīng)用中,網(wǎng)絡(luò)操作更多是使用 TCP 或 HTTP 協(xié)議。SocketServer.py 也提供了更高級(jí)的TCP、UDP封裝。下面就來(lái)看下關(guān)于TCP方面的網(wǎng)絡(luò)模塊(UDP和TCP的在代碼組織上差別不是特別大,暫且忽略)。
TCPServer
TCPServer 繼承了BaseServer,初始化的時(shí)候,進(jìn)行了socket套接字的創(chuàng)建。
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
self.server_bind()
self.server_activate()
__init__ 方法通過(guò) socket模塊創(chuàng)建了socket對(duì)象,然后進(jìn)行調(diào)用server_bind和server_activate。
server_bind
def server_bind(self):
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
server_bind 方法進(jìn)行socket對(duì)象的bind操作,以及設(shè)置socket相關(guān)屬性,如網(wǎng)絡(luò)地址的復(fù)用。
server_activate def server_activate(self): self.socket.listen(self.request_queue_size)
server_activate 方法也比較簡(jiǎn)單,添加socket對(duì)象的listen。
get_request
該類(lèi)最重要的方法就是 get_request。該方法進(jìn)行返回socket對(duì)象的請(qǐng)求連接。
def get_request(self): """Get the request and client address from the socket. """ return self.socket.accept()
get_request方法是在BaseServer基類(lèi)中的_handle_request_noblock中調(diào)用,從那里里傳入套接字對(duì)象獲取的連接信息。如果是UDPServer,這里獲取的就是UDP連接。
此外,TCPServer還提供了一個(gè) fileno 方法,提供給基類(lèi)的select調(diào)用返回文件描述符。
StreamRequestHandler
TCPServer實(shí)現(xiàn)了使用tcp套接字的網(wǎng)絡(luò)服務(wù),Handler方面則是對(duì)應(yīng)的StreamRequestHandler。它繼承了BaseRequestHandler?;?lèi)的setup方法和finish方法被它重寫(xiě),用于通過(guò)連接實(shí)現(xiàn)緩存文件的讀寫(xiě)操作。
setup方法:
def setup(self):
self.connection = self.request
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
self.wfile = self.connection.makefile('wb', self.wbufsize)
setup判斷了是否使用nagle算法。然后設(shè)置對(duì)應(yīng)的連接屬性。最重要的就是創(chuàng)建了一個(gè)可讀(rfile)和一個(gè)可寫(xiě)(wfile)的“文件”對(duì)象,他們實(shí)際上并不是創(chuàng)建了文件,而是封裝了讀取數(shù)據(jù)和發(fā)送數(shù)據(jù)的操作,抽象成為對(duì)文件的操作??梢岳斫鉃?self.rfile 就是讀取客戶(hù)端數(shù)據(jù)的對(duì)象,它有一些方法可以讀取數(shù)據(jù)。self.wfile則是用來(lái)發(fā)送數(shù)據(jù)給客戶(hù)端的對(duì)象。后面的操作,客戶(hù)端數(shù)據(jù)到來(lái)會(huì)被寫(xiě)入緩沖區(qū)可讀,需要向客戶(hù)端發(fā)送數(shù)據(jù)的時(shí)候,只需要向可寫(xiě)的文件中write數(shù)據(jù)即可。
實(shí)現(xiàn)TCP服務(wù)需要使用TCPServer和StreamRequestHandler共同協(xié)作。大致函數(shù)調(diào)用流程如下,函數(shù)調(diào)用用括號(hào)表示,賦值不帶括號(hào),沒(méi)有類(lèi)前綴的表示系統(tǒng)調(diào)用:
TCPServer - StreamRequestHandler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
TCPServer.socket = socket.socket(self.address_family, self.socket_type)
TCPServer.server_bind()
TCPServer.server_activate()
serve_forever():
select()
BaseServer._handle_request_noblock()
TCPServer.get_request() -> request, client_addres
socket.accept()
BaseServer.verify_request()
BaseServer.process_request()
BaseServer.process_request()
BaseServer.finish_request(request, client_address)
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
StreamRequestHandler.setup()
StreamRequestHandler.connection = StreamRequestHandler.request
StreamRequestHandler.rfile
StreamRequestHandler.wfile
BaseRequestHandler.handle()
StreamRequestHandler.finsih()
StreamRequestHandler.wfile.close()
StreamRequestHandler.rfile.close()
BaseServer.shutdown_request(request)
TCPServer.shutdown()
request.shutdown()
TCPServer.close_request(request)
request.close()
TCPServer.shutdown_request(request)
TCPServer.shutdown(request)
request.shutdown()
TCPServer.close_request(request)
request.close()
最早關(guān)于介紹BaseServer的時(shí)候,我們知道python對(duì)BaseServer設(shè)計(jì)的時(shí)候,預(yù)留了可用于Mixin擴(kuò)展多線(xiàn)程或多進(jìn)程的接口。mixin通過(guò)復(fù)寫(xiě)父類(lèi)的parse_request方法實(shí)現(xiàn)。
ThreadingMixIn
ThreadingMixIn 類(lèi)實(shí)現(xiàn)了多線(xiàn)程的方式,它只有兩個(gè)方法,分別是process_request和 process_request_thread方法。多進(jìn)程的方式是ForkingMixIn,暫且略過(guò)。
process_request
def process_request(self, request, client_address):
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
process_request方法復(fù)寫(xiě)了父類(lèi)的此方法。以此為接口入口,對(duì)每一個(gè)請(qǐng)求,調(diào)用Thread開(kāi)啟一個(gè)新的線(xiàn)程。每一個(gè)線(xiàn)程都綁定process_request_thread方法。
process_request_thread
def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
process_request_thread方法和BaseServer里的parse_request幾乎一樣。只不過(guò)是多線(xiàn)程的方式調(diào)用。
使用的時(shí)候,通過(guò)多繼承調(diào)用接口,例如:
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
具體的調(diào)用過(guò)程如下:
ThreadingMixIn -- TCPServer - StreamRequestHandler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
TCPServer.socket = socket.socket(self.address_family, self.socket_type)
TCPServer.server_bind()
TCPServer.server_activate()
serve_forever():
select()
BaseServer._handle_request_noblock()
TCPServer.get_request() -> request, client_addres
socket.accept()
BaseServer.verify_request()
BaseServer.process_request()
ThreadingMixIn.process_request()
t = threading.Thread(target = ThreadingMixIn.process_request_thread)
ThreadingMixIn.process_request_thread
BaseServer.finish_request(request, client_address)
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
StreamRequestHandler.setup()
StreamRequestHandler.connection = StreamRequestHandler.request
StreamRequestHandler.rfile
StreamRequestHandler.wfile
BaseRequestHandler.handle()
StreamRequestHandler.finsih()
StreamRequestHandler.wfile.close()
StreamRequestHandler.rfile.close()
BaseServer.shutdown_request(request)
TCPServer.shutdown()
request.shutdown()
TCPServer.close_request(request)
request.close()
TCPServer.shutdown_request(request)
TCPServer.shutdown(request)
request.shutdown()
TCPServer.close_request(request)
request.close()
- Python socket非阻塞模塊應(yīng)用示例
- Python socket模塊實(shí)現(xiàn)的udp通信功能示例
- Python基于socket模塊實(shí)現(xiàn)UDP通信功能示例
- Python使用SocketServer模塊編寫(xiě)基本服務(wù)器程序的教程
- 實(shí)例講解Python中SocketServer模塊處理網(wǎng)絡(luò)請(qǐng)求的用法
- Python的Asyncore異步Socket模塊及實(shí)現(xiàn)端口轉(zhuǎn)發(fā)的例子
- 在python中的socket模塊使用代理實(shí)例
- Python socket模塊方法實(shí)現(xiàn)詳解
相關(guān)文章
python寫(xiě)入csv時(shí)writerow()和writerows()函數(shù)簡(jiǎn)單示例
這篇文章主要給大家介紹了關(guān)于python寫(xiě)入csv時(shí)writerow()和writerows()函數(shù)的相關(guān)資料,writerows和writerow是Python中csv模塊中的兩個(gè)函數(shù),用于將數(shù)據(jù)寫(xiě)入CSV文件,需要的朋友可以參考下2023-07-07
Python中PyExecJS(執(zhí)行JS代碼庫(kù))的具體使用
pyexecjs是一個(gè)用Python來(lái)執(zhí)行JavaScript代碼的工具庫(kù),本文主要介紹了Python中PyExecJS(執(zhí)行JS代碼庫(kù))的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
Python決策樹(shù)分類(lèi)算法學(xué)習(xí)
這篇文章主要為大家詳細(xì)介紹了Python決策樹(shù)分類(lèi)算法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
python數(shù)據(jù)預(yù)處理之?dāng)?shù)據(jù)標(biāo)準(zhǔn)化的幾種處理方式
這篇文章主要介紹了python數(shù)據(jù)預(yù)處理之?dāng)?shù)據(jù)標(biāo)準(zhǔn)化的幾種處理方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
簡(jiǎn)單介紹一下pyinstaller打包以及安全性的實(shí)現(xiàn)
這篇文章主要介紹了簡(jiǎn)單介紹一下pyinstaller打包以及安全性的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
JavaScript實(shí)現(xiàn)一維數(shù)組轉(zhuǎn)化為二維數(shù)組
下面小編就為大家分享一篇JavaScript實(shí)現(xiàn)一維數(shù)組轉(zhuǎn)化為二維數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
Python實(shí)現(xiàn)爬取某站視頻彈幕并繪制詞云圖
這篇文章主要介紹了利用Python爬取某站的視頻彈幕,并將其繪制成詞云圖,文中的示例代碼講解詳細(xì),對(duì)我學(xué)習(xí)Python爬蟲(chóng)有一定的幫助,需要的朋友可以參考一下2021-12-12

