实践,用tornado实现自定义协议server
admin
2023-07-31 01:53:17
0

前言

俗话说\”光说不练假把式\”,上一篇文里都只是光看着别人的源码说,貌似有点纸上谈兵的意思.
所以这次写一个简单的,自己定义协议的server.
既可以熟悉Future和coroutine的用法,又可以在去除了复杂的http协议后,了解tornado的工作原理.
代码不多,加上空行和import也就200行不到.
在github上的源码点这里

目标

  • 1.定义一个简单的协议,达到远程调用的效果,并且是个长连接的协议(类似websock)
  • 2.模仿tornado的框架模式开发这个server框架,让用户代码开发方便,并且支持coroutine
  • 3.为了省去客户端的开发,客户端使用telnet

协议

  • 1.客户端连接成后,以换行符分割每次通信内容
  • 2.第一次通信内容是需要执行handler名称,第二次通信的内容是该handler的方法名
  • 3.对于客户端的主动close,需等待此连接所有的异步操作完成后才关闭连接
    最后运行方式如下图:

源码说明

总览

因为想让代码尽量少,所以委托模式没有严格按照设计模式的规范写,直接忽略掉了interface的定义.严格来说是需要定义interface和判断传入参数的类型的(泛华)
这是类的实例关系图(也不知道是不是这样画…)

MyServer和MyApplication的实例常驻.一个连接进来后就会创建图中其他的实例各一个.

异步说明

  • 1.为了达到目标中的第一点,需要一个while循环,读取了客户端数据后,执行handler,
    立即继续读取下一条客户端数据.直到客户端关闭操作,引发StreamClosedError才退出循环

  • 2.为了达到目标中的第二点,判断handler的返回值,如果类型是Future则yield处理,因为本方法有@gen.coroutine,所以yield就代表异步操作是在gen.Runner中执行的.

  • 3.为了达到目标中的第三点,需要记录每一个异步操作,并且异步操作完成后移除.当客户端主动关闭连接时,需判断是否还有future未完成.所以close代码中给每个future加上done_callback,用以检查关闭

详情见代码 MyServerConnection._server_request_loop

@gen.coroutine
    def _server_request_loop(self, delegate):
        try:
            #get request adepter
            request_delegate = delegate.on_request(self)
            while True:

                try:
                    message_future = self.stream.read_until_regex(b\"\\n\\r?\")
                    message = yield message_future
                    message = self._parse_data(message)

                except (iostream.StreamClosedError, 
                        iostream.UnsatisfiableReadError):
                    app_log.error(\' close the connect\')
                    self.close()
                    return

                except Exception:

                    gen_log.error(\"Uncaught exception\", exc_info=True)
                    self.close()
                    return

                ret = request_delegate.on_message(message)
                #如果是异步执行的方法,保存future,用于确保close时,所有future都已完成
                if isinstance(ret, Future):
                    ret.add_done_callback(lambda f:self._serving_futures.remove(f))
                    self._serving_futures.append(ret)

        finally:
            delegate.on_close(self)
    def close(self):

        def mayby_close(f):
            futures = self._serving_futures+self._pending_writes
            app_log.error(futures)
            if not any(futures):
                self.stream.close()

        pending_futrues = self._serving_futures+self._pending_writes
        if any(pending_futrues):
            map(lambda f:f.add_done_callback(mayby_close),pending_futrues)
        else:
            self.stream.close()

关于@coroutine

其实用@coroutine的时候只需要记住几点就行了
* 1.被包装的函数(方法),返回值是Future,
* 2.被包装的函数走完最后一行代码后,返回的Futurecallback就会被运行(因为在Runner中引发了StopIteration错误,被set_result了)
* 3.被包装的函数是在gen.Runner中运行的,而Runner是在ioloop(callback那块)中运行的

总结

代码非常简单,因为tornado为我们提供了异步的库(tornado真强大,协程好厉害!!),并且是单进程的编程,不需要考虑锁,写起来就更轻松了.
最后附上程序效果图

废话

这只是个吃饱撑着的程序,一点实际作用都没啊(好想被拍死!).吃饱撑着的原因是我还没下决心去找工作…工作太难找啦(哭~~)!!!!好想被带走……………..

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
项目管理和工程管理的区别 项目管理 项目管理,顾名思义就是专注于开发和完成项目的管理,以实现目标并满足成功标准和项目要求。 工...