最近想研究一下关于长链接的相关内容,在B站上看到了Zinx框架的视频,是Golang语言的框架,本着更好的理解框架的内容,按照整个Zinx课程的进度,制作一个Python版本的Zinx框架。有关于Zinx框架的具体内容,可以看框架作者的介绍。
python版本的Zinx,基于Gevent 22.10.2,使用协程功能。
golang版本的Zinx项目,项目中两个文件夹,ziface和znet。
- ziface主要是存放一些Zinx框架的全部模块的抽象层接口类。
- znet模块是zinx框架中网络相关功能的实现,所有网络相关模块都会定义在znet模块中。
└── zinx
├── ziface
│ └──
└── znet
├──
python中的关键字没有interface,但是可以使用抽象基类(abstract base class)和第三方库来实现类似于接口的功能。在实际开发中,我们可以根据具体需求选择合适的实现方式。
暂时使用抽象基类的形式模拟接口的实现。
本节开发在链接创建之后和链接关闭之前,可以执行用户自定义的函数。
在IServer中添加四个函数。
@abstractmethod
def SetOnConnStart(self, cb):
"""
设置该Server的连接创建时Hook函数
:param cb:
:return:
"""
pass
@abstractmethod
def SetOnConnStop(self, cb):
"""
设置该Server的连接断开时的Hook函数
:param cb:
:return:
"""
pass
@abstractmethod
def CallOnConnStart(self, conn: IConnection):
"""
调用连接OnConnStart Hook函数
:param conn:
:return:
"""
pass
@abstractmethod
def CallOnConnStop(self, conn: IConnection):
"""
调用连接OnConnStop Hook函数
:param conn:
:return:
"""
pass
在Server创建成员变量,将钩子函数定义一下。
class Server(IServer):
def __init__(self, name: str, ip: str, port: int,
family: socket.AddressFamily = socket.AF_INET,
socket_kind: socket.SocketKind = socket.SOCK_STREAM):
self.name: str = name
self.family: socket.AddressFamily = family
self.socket_kind: socket.SocketKind = socket_kind
self.ip: str = ip
self.port: int = port
# self.Router: Optional[IRouter] = None
self.msgHandler: IMsgHandler = NewMsgHandler()
self.connManager: IConManager = NewConnManager()
self.OnConnStart = None # 函数类型 钩子函数
self.OnConnStop = None # 函数类型 钩子函数
在Server创建函数,将钩子函数相关方法进行实现。
def SetOnConnStart(self, cb):
"""
设置该Server的连接创建时Hook函数
:param cb: 函数类型
:return:
"""
self.OnConnStart = cb
def SetOnConnStop(self, cb):
"""
设置该Server的连接断开时的Hook函数
:param cb:函数类型
:return:
"""
self.OnConnStop = cb
def CallOnConnStart(self, conn: IConnection):
"""
调用连接OnConnStart Hook函数
:param conn:
:return:
"""
if self.OnConnStart:
print("---> CallOnConnStart....")
self.OnConnStart(conn)
def CallOnConnStop(self, conn: IConnection):
"""
调用连接OnConnStop Hook函数
:param conn:
:return:
"""
if self.OnConnStop:
print("---> CallOnConnStart....")
self.OnConnStop(conn)
在Connection中设置钩子函数的运行位置。也就是Start函数和Stop函数。
def Start(self):
"""
启动链接 让当前的链接准备开始工作
:return:
"""
print("链接开启,ID=", self.ConnID)
# 开启写业务
g1 = gevent.spawn(self.StartWriter)
# 开启读业务
g2 = gevent.spawn(self.StartReader)
GlobalGevents.append(g1)
GlobalGevents.append(g2)
# 按照用户传递进来的创建连接时需要处理的业务,执行钩子方法
self.TcpServer.CallOnConnStart(self)
def Stop(self):
"""
停止链接 结束当前链接的工作
:return:
"""
print("链接关闭,ID=", self.ConnID)
if self.is_closed:
return
self.is_closed = True
# 如果用户注册了该链接的关闭回调业务,那么在此刻应该显示调用
self.TcpServer.CallOnConnStop(self)
# 关闭socket链接,回收资源
self.Conn.close()
# 将链接从连接管理器中删除
self.TcpServer.GetConnMgr().Remove(self)
服务接口做完了。在demo\hook中做一个客户端和服务端测试一下。
服务端代码。
# -*- coding: utf-8 -*-
import sys
sys.path.append("../../")
from znet.server import NewServer
from znet.router import BaseRouter
from ziface.irequest import IRequest
class PingRouter(BaseRouter):
"""
用户自定义路由
"""
def __init__(self):
super().__init__()
def Handle(self, request: IRequest):
"""
处理conn业务的方法
:param request:
:return:
"""
try:
print("调用Handle")
# request.GetConnection().GetTCPConnection().send("ping\n".encode("GB2312"))
print(request.GetData())
request.GetConnection().SendMsg(1, "ping\n".encode("GB2312"))
request.GetConnection().SendMsg(1, request.GetData())
except Exception as e:
print("ping异常", e)
def ConnectionStart(con):
print("======Start========", con.GetConnID())
def ConnectionStop(con):
print("======Stop========", con.GetConnID())
if __name__ == '__main__':
server = NewServer()
server.AddRouter(1, PingRouter())
server.SetOnConnStart(ConnectionStart)
server.SetOnConnStop(ConnectionStop)
server.Serve()
客户端代码与demo\msghandler一样即可。
此时发送和接收都正常。钩子模块完成。
网友评论