python count函数用法 comm_用Python操作nanomsg(四)——Pair&curses

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

98f0719c910d

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

98f0719c910d

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_TIMEOUT和C_RECV_TIMEOUT我都设置为200即200ms,需要根据不同情况进行设置。

最大的收获

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

98f0719c910d

看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

98f0719c910d

进入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

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

98f0719c910d

子命令的绑定函数

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

98f0719c910d

boot_loader4curses()源码

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

98f0719c910d

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

98f0719c910d

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下达发送消息指令。

98f0719c910d

image-20200214170536492.png

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

98f0719c910d

不用wrapper然后乱飞的样子

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

98f0719c910d

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

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

# _*_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上。

本系列其他文章:

内容

文章地址

说明

ReqRep

用Python操作nanomsg(五)——ReqRep

未开始

Survey

用Python操作nanomsg(六)——Survey

未开始

Bus

用Python操作nanomsg(七)——Bus

未开始

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值