欢迎光临散文网 会员登陆 & 注册

2022年的 Tcl/Tk 编程入门参考 11.通道

2022-04-24 20:43 作者:象喻  | 我要投稿

事件循环

Tcl语言一般是一条语句执行完毕后再执行下一条,但是像通道操作的话,有可能需要很长时间。于是一些通道命令有在后台运行的能力,不耽误其它语句的执行。但经常有些代码需要在通道操作完毕后再执行,也就是说:一边处理通道数据,一边执行一些代码,而后续代码要等到通道处理完毕再执行。面对这种情况就需要事件循环机制,通过vwait命令持续监视,直到事件结束再继续执行后续代码。

vwait

用法:vwait 标识变量名

vwait命令持续监视标识变量(即便变量尚未创建)并阻止后续代码的执行,直到变量值发生变化(对于尚未创建的变量来说,只要设置个值就算更改)。如果给出一个永远不会被修改的变量名,则会一直等待下去。

这个标识变量就相当于一个信号,后台操作完毕之后,修改一下这个标识变量,就等于告诉vwait事件已经结束了。否则就等于事件没有结束,vwait就继续持续监视标识变量,阻止后续代码执行。

⏱️监视变量,阻止后续代码运行
🔍变量值变更,vwait命令结束,退出事件循环
🌀虽然允许,但尽量避免嵌套调用vwait命令 (即嵌套事件循环)

示例代码请看 chan copy,嵌套事件循环和更多示例代码请看官方文档。


chan

chan命令是通道功能的大集合,也是一个比较尴尬的命令。在早期TCL语言里,跟通道相关的命令有十多个。从8.5版开始,其中大部分功能都集合到chan这一个命令里面了。但是,程序员们看了一下chan命令,功能一模一样,还得多打四个字母,然后……就没有然后了。

chan close <-> close

chan gets <-> gets

chan puts <-> puts

chan read <-> read

chan eof <-> eof

chan flush <-> flush

chan seek <-> seek

chan tell <-> tell

chan configure <-> fconfigure

chan copy <-> fcopy

chan event <-> fileevent

chan blocked <-> fblocked

"道理我都懂,不想多打4个字母……"


阻塞与非阻塞

对一个通道进行读写操作时,TCL语言默认会进入阻塞模式。也就是说,操作完成之前不会执行其它动作。好处是,可以降低程序的复杂性,一步一步来。但也有很大的局限性,比如说图形界面程序,在处于阻塞状态时,菜单、按钮都是不响应的。为了应对更多的情况,TCL语言还有非阻塞模式,同时可以做很多操作,比如网络程序,同时可以跟很多机器收发消息。


seek & tell

chan seek的功能是设定当前的访问位置(用文本文件举例的话,就是设定当前光标的位置)。

chan tell的功能是返回当前通道的访问位置。

chan seek 通道ID 偏移量 ?起始位置?

chan tell 通道ID

这两个跟seek与tell命令是一样的。

 


*提示:这两个命令无法用于命令管道(位置始终返回-1)。


chan configure

设置通道或查看当前设置(同fconfigure命令)。

chan  configure  通道ID  ?属性名?  ?属性值?  ?属性2  值2?...

属性:

-blocking 阻塞/非阻塞

-buffersize 缓冲区大小,默认4K

-encoding 字符编码

-buffering 缓冲区模式:full(默认)缓冲区满了再输出;line按行自动输出;none立即输出

-eofchar 特殊文件结束符(除DOS系统是 \x1a 外,其它都是空字符)

-translation 换行符:auto(自动)、lf(换行)、cr(回车)、crlf(回车换行)、binary(二进制)

 



chan copy

复制通道内容(同fcopy命令)。源通道与目标通道不同时(如字符编码、二进制)TCL会自动转换,但不保证转换结果。可以指定复制的字符数(不是字节数)。

复制时处于阻塞状态,但如果使用-command参数,则不会阻塞。此时chan copy会在后台运行,Tcl语言会继续执行后面的语句。复制完毕后,会调用-command后面指定的命令,并将数据总长度作为参数自动传递给命令(所以命令至少需要接收一个参数)。

如果预料到可能需要长时间才能完成的情况,则可以搭配vwait事件循环来使用。在调用命令中修改vwait监视变量的值,通知vwait正常退出。

 



chan event

在通道可读或可写时执行命令(即事件处理,同fileevent命令)

chan event 通道ID 读取或写入事件 ?事件处理命令?

这个功能监视通道的状态,当通道处于可读、或可写的状态时,就执行我们设定好的命令。它用于非阻塞模式(因为在阻塞状态时,TCL是不执行其它指令的),主要实现“当数据准备好时再进行操作”的功能。不加可选参数,单独使用时,返回当前已经设定的事件处理命令。加上可选参数,就设定可读、可写状态时的命令,需要注意的是,每个状态只能设定一条命令,如果再次设置新的命令,就会替换旧的命令。如果设置的事件处理命令是空字符串,那么就会删除之前的设定。



chan blocked

验证通道是否处于阻塞状态。如果通道被设置为非阻塞模式,则始终返回1。与 fblocked 命令功能相同

用法:chan blocked 通道ID


chan create

创建新的通道类型。

之前提到过四种通道:文件、网络、命令管道和串行通信设备通道 这个功能可以创建新的通道类型。在这个命令里我们要设定新通道类型的访问模式是读取还是写入,然后要按照规则把通道处理的代码先创建好,再作为参数传递给create。关于创建的部分,需要参考refchan文档,同时在这个文档里面也提供了一个创建“字符串读取通道”的例子,可以让我们直接把字符串读入到通道里——相当于创建了一个字符串类型的新通道。

新通道类型的创建参考官方文档 Tcl Commands > refchan

😶我是看文档才弄明白这功能到底干嘛用的,有需要的请自行钻研吧……


chan postevent

监控create创建的通道是否能够读/写。搭配 chan create 使用。

用法:chan postevent (chan create创建的通道ID) "参数列表(read/write)"

参考:wiki.tcl-lang.org/page/reflected+channel


chan names

默认返回全部通道名,也可以用字符串匹配(string match)的方式返回符合条件的通道名


chan pending

返回指定通道内部缓冲的字节数,用于防止恶意攻击(比如网络提交超大体积的数据)。

用法:chan pending 模式(input|output) 通道ID

它分别统计输入、输出数据量,如果通道没有开放某种模式(比如单写入或单读取),则返回-1


chan pip

官方文档说这个功能创建独立管道,由一个读取通道+一个写入通道组成。就是一面进,一面出,类似于一个管道一样。如果这个不好理解的话,也可以把这个功能想象成一个临时文件,这个文件有只读和只写两个通道。 先来看下具体例子:单独运行这个命令,会返回两个通道的ID,前面是读取通道,后面是写入通道。把两个通道ID赋值给对应的变量,写入内容,再用flush命令输出缓冲区,然后就可在读取通道读取内容了——就类似于一个临时文件一样。


这个功能,与tclx扩展库中的pipe命令功能类似。不像官方文档那么高端,tclx扩展库文档写的通俗易懂,它常用于两种情况:

一种是需要提前知道通道的ID,就是代码写到这儿,需要通道ID。但通道还没建立呢,就先用pipe创建独立管道,后续再把通道嫁接到这个独立管道上。

第二种情况是有几个不同属性的通道(比如字符编码不同),那么在转移数据的时候,我们希望能够手动处理一下,这个时候,也可以用这个功能。

当然,这是两种常见情况,只要了解独立管道的功能,在什么地方用都可以。


chan push

设定通道转换程序,把通道里的数据通过转换程序进行转换。 麻烦的地方在于这个转换程序必须得按照一定的规格来编写,就是说有固定格式的。在文档里专门有一章是介绍怎么创建转换程序。在这里我简单介绍一下,首先转换程序必须要有 initialize 和 finalize 两个子命令(“子命令”就是预设参数,比如chan push,push就相当于chan的子命令) 其中initialize部分,要以列表形式返回转换程序的全部子命令,这其中包括它自己和finalize。finalize在转换程序的最后运行。此外,如果是写入通道转换的话,转换程序必须包含write子命令;读取通道转换,必须包含read子命令。这两个子命令在读写通道时生效。

 


这个例子里先创建了UP转换命令,转换过程是在read子命令里完成的。然后把UP命令设置为通道的转换程序,在读取通道内容的时候,就可以看到,whoami命令的返回值,全都转换成大写了。


chan pop

取消通道转换,与chan push搭配使用

用法:chan pop 通道ID



chan truncate

截断文件通道内容(仅用于文件通道,并且要有写入权限)。在截断前会刷新缓冲区(类似flush命令)。

用法:chan truncate 通道ID ?长度?

如果省略长度参数,则以当前通道字节量为准

 




网络通道

Tcl语言使用socket命令建立网络通道,同样用chan命令进行配置。但是程序员需要注意网络通道的物理特性,比如传输速度、网络延迟/掉线等问题,这些网络特有的情况要有应对的代码来进行处理。

socket有两种模式,一种是建立网络通道的客户端模式,另一种是监听本机端口并做出应答的服务器端模式

客户端模式:socket ?额外参数? 网络主机名或IP地址 远程端口

额外参数: -myaddr 网络接口:使用指定的网络接口 -myport 端口:使用指定的端口(否则为系统分配端口) -async:异步模式

客户端模式建立的网络通道(TCP协议)默认使用当前系统的字符编码。

额外参数解释:-myaddr 指定网络接口,比如同时有多个网卡,具体用哪个就通过这个参数指定。

-myport 使用指定的本地端口,默认是系统自动分配端口

-async 异步模式——默认情况下socket命令在建立网络连接(即建立网络通道)之后才会返回,如果连接的过程比较满,程序就会卡住。异步模式就是socket命令立刻就会返回,程序可以继续运行,然后我们要通过事件处理功能,去监测网络通道是否可用。


服务器端模式:socket -server 自定义命令 ?-myaddr 网络接口? 本机端口

监听指定的本机端口,每当有客户端连接时,调用自定义命令,并为每个客户端创建网络通道。可选参数 -myaddr  跟客户端一样,用于有多个网卡时。

调用自定义命令时会自动提交 [通道ID] [客户端地址] [端口] 三个参数,也就是说自定义命令至少需要接收三个参数(或者使用args)

 







2022年的 Tcl/Tk 编程入门参考 11.通道的评论 (共 条)

分享到微博请遵守国家法律