2012-02-22 85 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