Python 學習筆記 - socketserver原始碼剖析
前面學習的例子都是單執行緒的socket收發;如果有多個使用者同時接入,那麼除了第一個連入的,後面的都會處於掛起等待的狀態,直到當前連線的客戶端斷開為止。
透過使用socketserver,我們可以實現併發的連線。
socketserver的使用很簡單:
首先看個簡單的例子
服務端:
自己定義一個類,繼承socketserver.baserequesthandler;
然後定義一個方法 handle()
然後透過socketserver.threadingTCPServer指定套接字和自己定義的類,每次當客戶端連入的時候,會自動例項化一個物件,然後透過server_forever()不斷迴圈讀寫資料。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author Yuan Li
import socketserver
class mysocketserver(socketserver.BaseRequestHandler):
def handle(self):
conn = self.request
conn.sendall(bytes("Welcome to the Test system.", encoding='utf-8'))
while True:
try:
data = conn.recv(1024)
if len(data) == 0: break
print("[%s] sends %s" % (self.client_address, data.decode()))
conn.sendall(data.upper())
except Exception:
break
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8009), mysocketserver)
server.serve_forever()
客戶端:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author Yuan Li
import socket
ip_port = ('127.0.0.1', 8009)
s = socket.socket()
s.connect(ip_port)
data = s.recv(1024)
print(data.decode())
while True:
send_data = input("Data>>>")
s.send(bytes(send_data, encoding='utf-8'))
recv_data = s.recv(1024)
print(recv_data.decode())
上面的效果是多個客戶端可以同時連入伺服器,輸入字母,返回大寫字母。
客戶端沒啥好說的,這個和單執行緒的操作一樣;但是伺服器咋一看很混亂。我們可以透過剖析原始碼來弄清他的執行過程。
這個類的基本結構是如下所示的,我們按照順序來跑一次看看他怎麼呼叫的
wKiom1gNmjTj7TH2AAK6-Px_JOg280.png
1. 首先執行的這句話,很明顯ThredingTCPServer是一個類,點進去看看他的例項化過程
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8009), mysocketserver)
2. 點著Ctrl鍵,點選這個類,PyCharm會自動開啟對應的原始碼,可以看見這個類又繼承了兩個父類ThredingMixIn和TCPServer
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
因為他的內容是pass,啥也沒做,根據繼承的順序,我們繼續往上(從左到右)找init建構函式;
3. ThreadingMixIn裡面沒有建構函式,那就繼續往右找,TCPServer裡面倒是有建構函式,但是他又呼叫了他父類BaseServer的建構函式,順著看上去,發現他就是封裝了幾個值在裡面,注意 self.RequestHandlerClass = RequestHandlerClass把我們自己定義的類傳進去了
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
4.接下來,在TCPServer的建構函式里面,他執行了bind,listen的操作,這個和單執行緒的操作是一樣的。到此為止,一個初始化的過程基本就完成了。
5.接下來,執行了server.serve_forever()的操作,我們看看內部是怎麼呼叫的。在這個函式里面,使用了selector的IO多路複用的技術,迴圈的讀取一個檔案的操作。接著呼叫了_handle_request_noblock()函式
try:
# XXX: Consider using another file descriptor or connecting to the
# socket to wake this up instead of polling. Polling reduces our
# responsiveness to a shutdown request and wastes cpu at all other
# times.
with _ServerSelector() as selector:
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request:
ready = selector.select(poll_interval)
if ready:
self._handle_request_noblock()
self.service_actions()
6.每次呼叫函式的時候都記住查詢的順序,從下往上,從左往右,最後在最上面的BaseServer再次找到這個函式,這個函式里面又呼叫了 process_request函式
"""
try:
request, client_address = self.get_request()
except OSError:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
一定要記住繼承的順序!!順序!!順序!!
因為baseserver自己有process_request的方法,ThreadingTCPServer也有同名的方法,當他呼叫的時候,按照順序,是執行的ThreadingTCPServer裡面的方法!!
wKioL1gNoxaThqRFAAB5Z_o7GWs657.png
wKiom1gNouDj48lSAABEODjRuN0521.png
可以看見他開了一個多執行緒
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
在他呼叫的process_request_thread裡面,他又呼叫了finsih_request
def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread.
In addition, exception handling is done here.
"""
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
finish_request裡面有對我們自定義的類做了一個例項化的操作
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
因為我們自定義的類沒有建構函式,他會去父類尋找,父類裡面會嘗試執行handle()方法,這就是為什麼我們需要在自定義的類裡面定義一個同名的方法,然後把所有需要執行的內容都放在這裡。
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
到此,socketserver一個完整的過程就結束了
©著作權歸作者所有:來自51CTO部落格作者beanxyz的原創作品,如需轉載,請註明出處,否則將追究法律責任
pythonsocketserverPython
0
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2820060/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Python物件初探(《Python原始碼剖析》筆記一)Python物件原始碼筆記
- Retrofit原始碼學習筆記原始碼筆記
- SocketServer 原始碼分析Server原始碼
- Python學習筆記—程式碼Python筆記
- jQuery原始碼學習筆記一jQuery原始碼筆記
- vue原始碼學習筆記1Vue原始碼筆記
- 學習筆記 sync/RWMutex原始碼筆記Mutex原始碼
- YYImage原始碼剖析與學習原始碼
- Spring-cloud學習筆記---Ribbon原始碼剖析之攔截器Interceptor方法SpringCloud筆記原始碼
- Spring-cloud學習筆記--- Eureka原始碼剖析之服務註冊介面SpringCloud筆記原始碼
- Redux 學習筆記 – 原始碼閱讀Redux筆記原始碼
- vue原始碼學習筆記2(resolveConstructorOptions)Vue原始碼筆記Struct
- JUC原始碼學習筆記6——ReentrantReadWriteLock原始碼筆記
- java.security.Provider 原始碼學習筆記JavaIDE原始碼筆記
- Python學習筆記Python筆記
- 【學習筆記】python筆記Python
- python原始碼閱讀筆記Python原始碼筆記
- Universal播放器的原始碼學習筆記播放器原始碼筆記
- async-validator 原始碼學習筆記(四):validator原始碼筆記
- MyBatis原始碼學習筆記(一) 初遇篇MyBatis原始碼筆記
- async-validator 原始碼學習筆記(三):rule原始碼筆記
- python學習筆記(1Python筆記
- Python學習筆記 - queuePython筆記
- Effective Python學習筆記Python筆記
- python——Matplotlib學習筆記Python筆記
- python學習筆記4Python筆記
- Python學習筆記 - asyncioPython筆記
- Python 學習筆記(一)Python筆記
- Python學習筆記 - aiohttpPython筆記AIHTTP
- python學習筆記(二)Python筆記
- Python學習筆記(2)Python筆記
- python——numpy學習筆記Python筆記
- Python學習筆記(三)Python筆記
- UE4(5)逆向學習筆記(三)——UEDumper原始碼學習筆記原始碼
- Python3學習筆記-字串和編碼Python筆記字串
- Python學習筆記|Python之程式Python筆記
- 《Android原始碼設計模式》學習筆記之ImageLoaderAndroid原始碼設計模式筆記
- Flutter筆記——runApp發生了什麼(原始碼學習)Flutter筆記APP原始碼