美文网首页
用Python操作nanomsg(四)——Pair&curses

用Python操作nanomsg(四)——Pair&curses

作者: 钢琴师2005 | 来源:发表于2020-02-14 20:34 被阅读0次

先上仓库地址 https://github.com/r00t1900/nano-chat.git

按照之前的节奏,第四章应该就是介绍PAIR模式的用法并且顺利更新之前的LAN-Chat Program后就可以完事儿了。今天这章到今天才完成,实属是自己一根筋,想着把curses利用进来,让命令行程序的可操作性更好一些。


tmux左右分屏展示程序运行的样子 nano-chat

最初的“梦想”实现了,就是花的时间太长了,看git的提交记录才发现在curses库整合测试成功的这条commit之前整整三天没有提交过一次commit下面来一点一点跟大家掰这个程序的完成过程,和完事儿后一些自己的思考。

关于Pair

此模式的用法很简单,新建一个pair模式socket所需要的代码为:

pair_socket = nnpy.Socket(nnpy.AF_SP, nnpy.PAIR)
# set send and recv timeout to 1s
pair_socket.setsockopt(nnpy.SOL_SOCKET, nnpy.SNDTIMEO, config.C_SEND_TIMEOUT)
pair_socket.setsockopt(nnpy.SOL_SOCKET, nnpy.RCVTIMEO, config.C_RECV_TIMEOUT)

其中的C_SEND_TIMEOUTC_RECV_TIMEOUT我都设置为200即200ms,需要根据不同情况进行设置。

最大的收获

说实话这一次写(写代码的写,不是写本文的写),私以为劳动量还是挺大的。


看commit时间才知道被curses耽误了许久

以前写一些分析、数据爬取或者CV类的系统动辄就是千行以上,而这次的整个代码规模估计没有超过500行但是依然觉得劳动量比之前写过的都大,无外乎是因为以前写的项目其实是冗余太多、dulicated code fragment太多、结构设计不好的项目罢了,那时的perfect这时看来只能是normal。而现在,懂得了控制规模、优化代码,更重要的一点是:代码项目一定要有结构,并且尽量把这个结构做到最好。在后期规模扩大的时候感觉非常明显,结构设计不好,就只能空有一丝重构的冲动了:

对,只能是空有——冲动是因为觉得现在版本写的太烂;空有是重构后未必能写的没这么烂所以又不敢动,你能保证这一版本用来绕过大部分bugs的代码不会在你重构的时候卡擦卡擦给KO了?

不要轻易重构,绝对真理。

回顾

讲了这么多闲的,接着讲这次的journery of curses & nanomsg programming

目录结构

先来讲一下目录分工,使用命令tree -h -I __pycache__

.
├── [2.4K] chat.py
├── [4.0K] conf
│ └── [2.6K] config.py
├── [4.0K] logs
├── [4.0K] modules
│ ├── [ 966] common.py
│ ├── [4.0K] communication
│ │ ├── [ 122] __init__.py
│ │ └── [5.2K] nanomsg_pair.py
│ ├── [ 121] __init__.py
│ ├── [2.5K] logger.py
│ ├── [1.3K] tools.py
│ └── [4.0K] ui
│ ├── [ 576] cmd.py
│ ├── [10.0K] curses.py
│ ├── [ 122] __init__.py
│ └── [ 23K] windows.py
└── [4.0K] tools
├── [ 241] curses_demo_client.py
├── [ 241] curses_demo_server.py
└── [ 364] find_key_name.py

6 directories, 15 files

整个程序目录分为5个部分:配置、日志、模块、工具和程序入口,

分别对应conf、logs、modules、tools和chat.py,使用方法还是跟早期版本一样,运行程序入口chat.py,并使用子命令对号入座,如果一切配置都没问题,那么就顺利进入curses产生的交互界面了:

$ python3 chat.py bind tcp 127.0.0.1:2020
进入curses产生的交互界面

按Ctrl + D退出时只显示:

$ python3 chat.py bind tcp 127.0.0.1:2020
comm module stopped:1

则表示成功关闭了nanomsg,这点非常重要,因为如果没有成功关闭将会造成资源占用。

(勘误:图中的帮助Ctrl- ^D后来发现有误,^D就已经代表了Ctrl + D了)

Code Level1

因为具体变动是在子命令的绑定函数,我们来看看子命令的绑定函数现在是神马样子:

子命令的绑定函数

可以见到这里我都用了一个boot_loader4curses()的方法来接替执行,区别在于两者参数is_server的值,Ctrl + B接着看boot_loader4curses():

boot_loader4curses()源码

这里看到里面初始化了一个PairObject类为comm_module,在其start()方法中我们看到根据is_server的值进行了bindconnect之间的选择:

start()方法是establish()方法的异常捕捉版本 establish()方法

接着回到boot_loader4curses(),在初始化完成以后,我新建了一个chat_logs列表变量,用于curses的界面显示与comm_module之间的数据共享(无奈之举,想用管道但是不会),comm_module需要将接收到的消息存入chat_logs中让curses去显示在屏幕上,所以在这里用enable_recv_loop()方法启动了一个线程来接收消息;相反,发送的消息却不一定要经过comm_module,因为键盘输入一开始就是curses在接管,因此不必将发送循环也外联到chat_logs上,直接连接到curses中即可,从而非常正常+正确地落实了从curses向comm_module下达发送消息指令

image-20200214170536492.png

然后该提前准备的都准备好了,就可以启动curses界面了,即239行的wrapper函数,这是curses的内置函数,通过from curses import wrapper引入,用途是简化一些初始化操作,什么cbreak()、noecho()、keypad()这些原先都是需要预先设置,并且在完毕以后按着镜像顺序归零。只要程序有error,结束以后命令行界面就是一顿乱飞如:

不用wrapper然后乱飞的样子

而使用了wrapper以后则会优雅地返回出错详细,虽然真正到curses时它报的错误贯彻了C的风格言简意赅没什么用但至少终端还健在:


这就比较友好了,相对来说

上述的测试代码如下,注释到最后两行中的任意一行以切换测试内容:

# _*_coding:utf-8 _*_
# @Time    : 2020/2/14 17:19
# @Author  : Shek 
# @FileName: fuzz_test.py
# @Software: PyCharm
import curses
from curses import wrapper


def main(stdscr):
    # Clear screen
    stdscr.clear()

    # This raises ZeroDivisionError when i == 10.
    for i in range(0, 11):
        v = i - 10
        stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10 / v))

    stdscr.refresh()
    stdscr.getkey()


def run_with_wrapper():
    wrapper(main)


def run_without_wrapper():
    std_scr = curses.initscr()
    curses.noecho()
    curses.cbreak()
    std_scr.keypad(1)

    main(stdscr=std_scr)

    std_scr.keypad(0)
    curses.nocbreak()
    curses.echo()
    curses.endwin()


run_with_wrapper()
# run_without_wrapper()

好了wrapper()里的函数放在后面专门讲,先接着往下。我们看到:

comm_module.enable_recv_loop()
comm_module.start_recv_loop(chat_var=chat_logs)
wrapper(main4curses_wrapper, comm_module, chat_logs)
comm_module.stop()  # always remember to call this after wrapper

在wrapper出来以后,一定要记得前面说的stop掉comm_module,不然地址资源不释放,你发现你的chrome都上不了网只能玩小恐龙,而微信、QQ却都还能用这里之所以不在wrapper里面call这个stop()方法是因为在wrapper里面的所有print在你出来以后都是看不见的,即使在还在wrapper里面享用着curses界面时也看不见因为那时候curses已经在画画了而你的print早就被cover了。而能否stop掉comm_module又比较重要,所以我把这个放在外面,可以明确知道是否正确关闭了nanomsg。

以上是我认为的第一层结构,其特点可以梳理如下:

特点 说明
入口比较简单 两个子命令绑定在boot_loader4curses()上
相互调用比较少 单方向调用,基本没有涉及对象间交互调用
后续关联单一、清晰 boot_loader4curses()内的关键点非常清晰:就是239行的wrapper()
关联对象、函数尚未变复杂 只剩下main4curses_wrapper()和comm_module

接下的code level2可以大胆地从wrapper()入手,之前固化的代码片段不会因为wrapper()中下一步发展而有大改动。

Code level2

(有人问我的截图里怎么注释都是英文……我回答一下:只是为了方便、简洁、不用调输入法嗖地一下就好了哈哈)

未完待续...

Structure Summary

下面用表格给大家梳理了一下:

名称 位置 类型 说明
chat.py . file 入口代码:提供命令行子命令选项与用户交互,而后进入curses交互
conf . directory 配置目录:存放配置文件(默认为config.py)
modules . directory 模块目录:存放通信、界面显示和日志记录等模块代码,用于调用
tools . directory 工具目录:存放用于测试的一键运行脚本,比如获取按键ASCII及其curses按键名等
logs . directory 日志保存目录:用于早期版本,近期版本因为引入了curses暂时未集成logger模块,故logs目录为空,暂无实际使用意义。
config.py ./conf file 默认配置文件,各常量均以大写命名,具有可读性,如:C_SHOW_POSITION_SIZE = True,C表示给curses相关函数使用,SHOW_POSITION_SIZE代表是否在窗口栏显示定位坐标和窗口大小。
common.py ./modules file 通用模块。
函数:
cn_count()
current_datetime()
logger.py ./modules file 日志记录模块,用于同时记录日志到本地文件和终端。
类:Logger
tools.py ./modules file 工具函数模块,放置了tools下脚本需要用到的函数。
函数:
print_key_name()
curses_ui_test_server()
curses_ui_test_client()
ui ./modules directory 界面显示子模块目录。
communication ./modules directory 通信子模块目录。
cmd.py ./modules/ui file 子命令模块,包含两个子命令的调用函数。
函数:
sub_cmd_bind()
sub_cmd_connect()
windows.py ./moudles/ui file 窗口模块,用于创建curses窗口布局,移除了原生curses坐标系统先y后x的反人类设定,更加易于使用。
类:
CreateWindow
StatusWindow
ChatWindow
SendWindow
DebugWindow
HelpWindow
curses.py ./modules/ui file curses启动模块,用于辅助启动curses。
函数:
process_chinese_characters()
color_pair_configure()
pre_configure()
key_pressed_solution()
main4curses_wrapper()
boot_loader4curses()
nanomsg_pair.py ./module/
communication
file nanomsg(pair)通信模块,用于实际执行网络数据通信,优化了原生C-nanomsg的蜜汁报错,提高执行效率。
类:
PairObject

改进

完善模块通用性、易用性,移植到Qt5和PyQt5上。

本系列其他文章:

内容 文章地址 说明
准备 用Python操作nanomsg(一)——准备 2020.2.7更新
PipeLine 用Python操作nanomsg(二)——PipeLine 2020.2.7更新
PushPub 用Python操作nanomsg(三)——PubSub 2020.2.8更新
ReqRep 用Python操作nanomsg(五)——ReqRep 未开始
Survey 用Python操作nanomsg(六)——Survey 未开始
Bus 用Python操作nanomsg(七)——Bus 未开始

相关文章

网友评论

      本文标题:用Python操作nanomsg(四)——Pair&curses

      本文链接:https://www.haomeiwen.com/subject/pqvjfhtx.html