我正在研究Tcl套接字通信,我实际上正在评估java客户端发送到tcl服务器的命令。在每个客户的新请求中,我创建新的interp
并在连接关闭时删除它。
到目前为止,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: 如何与从属主翻译进行交互,反之亦然。说,我必须从主人的全球空间访问变量。那可能吗 ?在此之前,我现在遵循的方法是一个好的方法吗?还有其他建议或改进吗?
答案 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)
当异常情况发生时,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"
}