2012-02-22 86 views
2

有没有办法从fileevent处理程序处理非全局变量?考虑以下最低服务器:从fileevent处理程序处理非全局变量

proc initState {stateName} { 
    upvar $stateName state 
    set state(foo) bar 
    set state(baz) bla 
    # ... 
    return 
} 

proc handleConnection {stateName newsock clientAddress clientPort} { 
    upvar $stateName state 
    fconfigure $newsock -blocking 0 
    fconfigure $newsock -buffering line 
    fileevent $newsock readable [list handleData $newsock] 
    return 
} 

proc handleData {f} { 
    if {[eof $f]} { 
     fileevent $f readable {} 
     close $f 
     return 
    } 
    gets $f line 
    puts $f ok 
    # need to modify state here... 
    return 
} 

proc runServer {port} { 
    array set state {} 
    initState state 
    socket -server {handleConnection state} $port 
    vwait forever 
} 

runServer 1234 

是否有可能操纵在runServer范围内创建的state阵列或者是做这做state一个全局变量的唯一途径?

我很新的Tcl,如果我使用C我只是将一个指针state传递到事件处理程序,但不幸的是,Tcl不允许这样做。我在这里做了什么奇怪的事情,还有更多的Tcl-ish方式吗?

回答

4

这根本不起作用。问题是Tcl的栈帧不能以你想要的方式持续存在。

的标准选项来解决此是:

  1. 保持状态在全局数组由“连接标志”索引(例如,信道的名称)。请记住,数组是按字符串索引的;像“sock42,hostname”这样的复合键是非常合法的。

  2. 将状态保持在以连接标记命名的命名空间中。如果您使用的是Tcl 8.5,那么namespace upvar命令使得这个更容易。

  3. 保持状态在TclOO 对象(需要的Tcl 8.6 单独TclOO包8.5)或使用不同的对象系统(例如,[INCR Tcl的],XOTcl;这些是可用于许多的Tcl版本)。

  4. 将状态保持在协程(需要Tcl 8.6)。这有效地为您提供了一个命名堆栈(并且可以让您编写代码,因此它显然是“直线”而不是回调驱动的),但其版本要求为严格

+0

感谢您的全面解释,为此使用命名空间是一个有趣的可能性。 – user1226159 2012-02-22 17:48:05

+0

@user另一种可能性是为每个连接生成一个唯一的标记名称,并调用全局数组,使用'upvar#0 $ token ary;用$ ary(this)和$ ary(that)'做些什么。这就是Tcl的'http'包的工作原理。 – 2012-02-22 20:12:32