??最近想研究一下關(guān)于長鏈接的相關(guān)內(nèi)容,在B站上看到了Zinx框架的視頻,是Golang語言的框架,本著更好的理解框架的內(nèi)容,按照整個Zinx課程的進度,制作一個Python版本的Zinx框架。有關(guān)于Zinx框架的具體內(nèi)容,可以看框架作者的介紹。
??python版本的Zinx,基于Gevent 22.10.2,使用協(xié)程功能。
??golang版本的Zinx項目,項目中兩個文件夾,ziface和znet。
- ziface主要是存放一些Zinx框架的全部模塊的抽象層接口類。
- znet模塊是zinx框架中網(wǎng)絡(luò)相關(guān)功能的實現(xiàn),所有網(wǎng)絡(luò)相關(guān)模塊都會定義在znet模塊中。
└── zinx
?├── ziface
?│??└──
?└── znet
????├──
??python中的關(guān)鍵字沒有interface,但是可以使用抽象基類(abstract base class)和第三方庫來實現(xiàn)類似于接口的功能。在實際開發(fā)中,我們可以根據(jù)具體需求選擇合適的實現(xiàn)方式。
??暫時使用抽象基類的形式模擬接口的實現(xiàn)。
??在之前的章節(jié)中,消息讀到之后直接就進行了寫操作。本節(jié)就將寫操作分離出來。需要使用Queue隊列進行存儲即將需要寫的數(shù)據(jù)。本節(jié)內(nèi)容都在Connection中完成。
??在Connection中添加一個成員變量。
from gevent.queue import Queue
class Connection(IConnection):
def __init__(self, conn: socket.socket, connID: int, remote_addr: tuple, msgHandler: IMsgHandler):
self.Conn: socket.socket = conn # 當前鏈接的socket TCP套接字
self.ConnID: int = connID # 鏈接的ID
# self.HandlerAPI = handlerAPI # 當前鏈接綁定的業(yè)務(wù)處理方法的API
self.is_closed: bool = False # 鏈接狀態(tài)
self.Remote_Addr: tuple = remote_addr # 地址
# self.Router: IRouter = router
self.msgHandler: IMsgHandler = msgHandler # 消息處理模塊
self.msgQueue: Queue = Queue() # 寫隊列
??修改一下SendMsg,原來的SendMsg直接就將消息寫出去了,現(xiàn)在需要將消息存入msgQueue中。
def SendMsg(self, msgID: int, data: bytes):
"""
發(fā)送數(shù)據(jù) 將數(shù)據(jù)發(fā)送給遠程的客戶端
:param msgID:
:param data:
:return:
"""
if self.is_closed:
raise Exception("發(fā)送數(shù)據(jù)時客戶端鏈接被關(guān)閉")
try:
dp = NewDataPack()
msg = dp.Pack(NewMessage(msgID, len(data), data))
# self.Conn.send(msg)
self.msgQueue.put(msg)
except Exception as e:
print(e)
??新增一個StartWriter函數(shù),實現(xiàn)寫功能。
def StartWriter(self):
"""
寫業(yè)務(wù)
:return:
"""
print("開啟寫業(yè)務(wù)")
while True:
try:
if self.is_closed:
break
msg = self.msgQueue.get_nowait()
self.Conn.send(msg)
except:
# 由于msgQueue的get_nowait并不會主動切換其他協(xié)程,使用sleep切換到其他協(xié)程,保證不在當前協(xié)程中循環(huán)運行
gevent.sleep(0)
continue
print(self.Remote_Addr, "寫業(yè)務(wù)退出")
??在Start函數(shù)中,開啟寫功能。
def Start(self):
"""
啟動鏈接 讓當前的鏈接準備開始工作
:return:
"""
print("鏈接開啟,ID=", self.ConnID)
# 開啟寫業(yè)務(wù)
g1 = gevent.spawn(self.StartWriter)
# 開啟讀業(yè)務(wù)
g2 = gevent.spawn(self.StartReader)
GlobalGevents.append(g1)
GlobalGevents.append(g2)
??服務(wù)接口做完了。在demo\read_write中做一個客戶端和服務(wù)端測試一下,代碼與demo\msghandler一樣即可。
??此時發(fā)送和接收都正常。讀寫分離完成。