【问题标题】:Communication between master interpreter and slave interpreters in TclTcl 中主解释器和从解释器之间的通信
【发布时间】:2015-03-20 10:11:33
【问题描述】:

我正在研究 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: 如何与从属的主解释器交互,反之亦然。说,我必须从主人的全局空间访问一个变量。那可能吗 ?在此之前,无论我现在采用的方法是一种好方法吗?还有其他建议或改进吗?

【问题讨论】:

  • 除了在catch 周围不使用大括号,就像在if {[catch {…} result]} { 中一样?对我来说没有什么明显的。
  • 对不起。错字。现已更正。想知道我是否在客户端-服务器通信中犯了错误。顺便说一句,我仅从stackoverflow.com/a/16408527/974155 遵循您的常规方法。将很快用正确的代码更新帖子。给我1小时。我正在旅行。
  • @DonalFellows :我设法解决了catch 问题。这是工作。我已经更新了我的问题。还有一些其他问题。

标签: sockets tcl interpreter


【解决方案1】:

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

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

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 是个例外,它实际上是全局共享的。仅将其用于将信息传递给子进程或从操作系统获取信息;它比几乎所有其他方法都慢得多。)

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

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"
}

【讨论】:

  • @Donal Fellows:感谢您的明确解释。这帮助很大。
【解决方案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

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

您也许可以使用从站中的跟踪来提供直接变量访问的错觉。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-25
    • 1970-01-01
    相关资源
    最近更新 更多