Tcl中主解释器和从解释器之间的通信

时间:2015-03-20 10:11:34

标签: sockets tcl interpreter

我正在研究Tcl套接字通信,我实际上正在评估java客户端发送到tcl服务器的命令。在每个客户的新请求中,我创建新的interp并在连接关闭时删除它。

基本代码参考其他question& answer

到目前为止,Java代码没有问题。

proc writeJavaUTF {stream string} {
     set data [encoding convertto utf-8 $string]
     if {[string length $data] > 0xffff} {
         error "string to long after encoding"
     }
     set len [binary format S [string length $data]]
     puts -nonewline $stream $len$data
     flush $stream
 }

 proc readJavaUTF {stream} {
     binary scan [read $stream 2] S len
     if {[info exists len]} {
     set data [read $stream [expr {$len & 0xffff}]]
     return [encoding convertfrom utf-8 $data]
     } else {
        return "NULL"
     }
 }


set svcPort 9999

proc svcHandler {sock} {
  global tclEngine
  puts "receiving socket request from $sock"
  set userTclCmd [readJavaUTF $sock]; # Reading the user commands over socket 
  puts "The command received from user : $userTclCmd"
  if {[eof $sock]} {
     puts "Socket $sock is closing it's connection. Going to delete it's interpreter"
     interp delete $tclEngine($sock)
     close $sock
  } else {
    catch {interp eval $tclEngine($sock) $userTclCmd} cmdResponse
    puts "My response : $cmdResponse"
    writeJavaUTF $sock $cmdResponse; # Writing the response over socket 
  }
}
proc accept {sock addr port} {
  # Once connection is made, then creating a new slave interpreter 
  # for the client
  global tclEngine
  set tclEngine($sock) [interp create]
  fileevent $sock readable [list svcHandler $sock]
  fconfigure $sock -buffering line -blocking 0 -translation binary
  puts "Accepted socket connection from $addr on port $port "
}

#Tcl Array Engine to hold reference of all the client Tcl interpreters
array set tclEngine {}
# Listening for client requests
socket -server accept $svcPort
puts "I am waiting ..."
vwait events

使用readJavaUTF,我已经从我这边添加了一张支票。

if {[info exists len]} {

}

问题1: 当客户端关闭连接时,此时client.close()还会调用readJavaUTF。为什么会这样 ?早些时候,我已经明确地使用了你的版本(即Mr.Donal'代码)answer

但是,我收到了以下错误

can't read "len": no such variable
    while executing
"expr {$len & 0xffff}"
    (procedure "readJavaUTF" line 4)
    invoked from within
"readJavaUTF $sock"
    (procedure "svcHandler" line 4)
    invoked from within
"svcHandler sock280"

为了解决这个问题,我刚刚添加了这段代码。

问题2: 如何与从属主翻译进行交互,反之亦然。说,我必须从主人的全球空间访问变量。那可能吗 ?在此之前,我现在遵循的方法是一个好的方法吗?还有其他建议或改进吗?

2 个答案:

答案 0 :(得分:2)

您无法直接访问主解释器中的变量。您需要在主服务器中实现一个命令,并在从服务器中使用别名来执行此操作。像

这样的东西
% interp alias slave shadow {} variable_access
shadow
% proc variable_access {var args} { uplevel #0 [list set $var {*}$args] }
% set x 2
2
% slave eval {shadow x}
2
% slave eval {shadow x 5}
5
% slave eval {shadow x}
5

您不希望使用上述内容,因为您将失去所有保护。检查允许的特定变量名称,并且可能只允许读取。

您可以使用从站中的跟踪提供直接变量访问的假象。

答案 1 :(得分:2)

第一个问题:fileevent回调中的文件结束处理

当异常情况发生时,Tcl调用read回调。在执行eof后,您应该使用read命令检查这类事情。

set data [read $stream 2]
if {[string length $data] == 0 && [eof $data]} {
    # Closing unregisters all fileevent handlers too
    close $stream
    return
}
binary scan $data S len

第二个问题:在口译员之间共享变量

根本不在解释器之间共享变量。尽管Tcl具有全局变量并且使用它们非常多,但它们仅仅 全局到该解释器。所有其他口译员都有自己的变量。 (例外是env,它实际上是全局共享的。只用它来传递信息到子进程或从操作系统获取信息;它比几乎所有其他方法慢得多。)< / p>

命令可以在从解释器中别名(带interp alias),以便它们可以由主解释器实现。这种机制有点类似于系统调用的工作方式,让从属设备可以访问精确可控的功能配置文件;任何没有别名的东西(而不是奴隶的内部实施)是完全无法实现的。然后,您可以轻松地使用它以安全的方式实现对主变量的访问。

proc read_my_vars {permittedList varName {value ""}} {
    if {$varName ni $permittedList} {
        return -code error "no such variable \"$varName\""
    }
    upvar "#0" $varName var
    return $var
}
proc write_my_vars {permittedList varName value} {
    if {$varName ni $permittedList} {
        return -code error "no such variable \"$varName\""
    }
    upvar "#0" $varName var
    set var $value
}

interp alias $slave READ {} read_my_vars {a b c}
interp alias $slave WRITE {} write_my_vars {a b c}
set a 1
set b 2
set c 3

$slave eval {
    puts "a = [READ a]"
}

您甚至可以在奴隶中使用跟踪来使其透明:

$slave eval {
    trace add variable a read {apply {args {READ a}}}
    trace add variable a write {apply {args {global a;WRITE a $a}}}
}

$slave eval {
    puts "a = $a"
}