我正在尝试构建的应用程序如下。
Java客户端< ---> TCL服务器(比如Server.tcl)< --->其他一些TCL脚本(比如ABC.tcl)
我正在使用Socket编程来实现Java Client和TCL服务器之间的通信。现在我的要求是在运行时从Server.tcl调用ABC.tcl。执行时ABC.tcl将通过server.tcl向Java客户端发送一些问题。坐在Java客户端的用户应该将回复发送给问题,答案应该回到ABC.tcl。现在,当回复回来时,ABC.tcl应该等待。但我不能在ABC.tcl中有一个繁忙的等待循环,因为控件不会来到Server.tcl,因为它是曾经与Java客户端通信的人。
ABC.tcl将使用Server.tcl中定义的一些过程。如果需要,可以更改设计。
我在考虑在TCL中使用Threads。我打算将ABC.tcl作为一个单独的线程调用,然后让它等待回复。这样Server.tcl可以与ABC.tcl分开执行。
他们的意见是他们有更好的方法吗? 如果Threads是唯一更好的方法,那么我可能遇到的挑战是什么?如何处理它们?
编辑:
我没有在Server.tcl和ABC.tcl之间使用套接字通信。 在Server.tcl中,我正在寻找ABC.tcl,当一些通信请求来自Java客户端时,我正在调用ABC.tcl的一个过程(比如proc someProc {})。 现在在someProc过程中,完成了一些处理,并通过Server.tcl将问题(例如“你想继续吗?”)发送回Java客户端。 现在在ABC.tcl我希望程序someProc等到用户输入内容。当用户输入答案时(是/否), 我希望程序从等待用户输入的位置继续执行。
现在发生的事情是我无法按要求正确设置等待条件。我尝试使用一个标志变量,当用户输入答案时将更新该标志变量,并尝试根据标志变量在someProc中保持繁忙的等待循环。但是当用户输入到来时,它不会从等待循环开始继续执行。它像普通请求一样启动,进入Server.tcl中注册的过程来处理传入的数据。
我有“vwait事件”调用,并且我在Server.tcl中有“fileevent $ sock readable [list svcHandler $ sock]”。
我无法得到,我怎样才能在ABC.tcl中的someProc过程中等待,并根据用户输入从相同的忙等待中恢复处理。
source "<path>/serverTest.tcl"
set svcPort [lindex $argv 0]
set filePath <path where all related files are kept>
set replyReady 0
set packet ""
proc writeToFile {fileName data mode} {
set fileId [open $fileName $mode]
puts -nonewline $fileId $data
close $fileId
}
proc writeJavaUTF {sock msg} {
set date [exec date]
set msg $date$msg
set data [encoding convertto utf-8 $msg]
puts -nonewline $sock [binary format "S" [string length $data]]$data
}
proc readJavaUTF {sock} {
binary scan [read $sock 2] "S" len
set data [read $sock [expr {$len & 0xFFFF}]]
return [encoding convertfrom utf-8 $data]
}
proc sendMessageToUser {sock msg} {
writeToFile [exec pwd]/CTS/log "\nSending Message Back to Client" "a"
writeJavaUTF $sock $msg\n
}
proc setReplyReady {} {
global replyReady
set replyReady 1
writeToFile [exec pwd]/CTS/log "\nSetReplyReady" "a"
}
proc resetReplyReady {} {
global replyReady
set replyReady 0
writeToFile [exec pwd]/CTS/log "\nResetReplyReady" "a"
}
proc getReplyReadyStatus {} {
global replyReady
writeToFile [exec pwd]/CTS/log "\nReply Ready is : $replyReady" "a"
return $replyReady
}
proc executeCommand {sock msg} {
writeToFile [exec pwd]/CTS/log "\nexecuteCommand" "a"
runServerTest $sock
}
# Handles the input from the client and client shutdown
proc svcHandler {sock} {
global packet replyReady
set packet [readJavaUTF $sock] ;# get the client packet
writeToFile [exec pwd]/CTS/log "\nThe packet from the client is $packet" "w"
set packet [string range $packet 0 [expr {[string first "\n" $packet] - 1}]]
set endChar "EOC"
if {[eof $sock] || ![string compare -nocase $packet $endChar]} { ;# client gone or finished
writeToFile [exec pwd]/CTS/log "Closing the socket" "a"
writeToFile [exec pwd]/CTS/flag "0" "w"
close $sock ;# release the servers client channel
exit
} else {
#doService $sock $ipacket
set typeReply "reply"
set typeExecute "exec"
set type [string range $packet 0 [expr {[string first ":" $packet] - 1}]]
writeToFile [exec pwd]/CTS/log "\nThe type is : $type" "a"
# If it is not a reply for a request
if {![string compare -nocase $type $typeExecute]} {
executeCommand $sock $packet
} else {
writeToFile [exec pwd]/CTS/log "\nExecuting the ELSE" "a"
setReplyReady
}
}
}
proc accept {sock addr port} {
# Setup handler for future communication on client socket
fileevent $sock readable [list svcHandler $sock]
# Read client input in lines, disable blocking I/O
fconfigure $sock -buffering line -blocking 0 -translation binary
return $sock
}
set sock [socket -server accept $svcPort]
vwait events ;# handle events till variable events is set
proc runServerTest {sock} {
global replyReady
writeToFile [exec pwd]/CTS/log "\nInside serverTest.tcl." "a"
resetReplyReady
sendMessageToUser $sock "From Server : The socket is working. Do you want to continue ?"
writeToFile [exec pwd]/CTS/log "\nWaiting for user input" "a"
#Loop untill the reply is read from the socket
while {![getReplyReadyStatus]} {
after 1000
}
set retMessage [getPacket]
resetReplyReady
writeToFile [exec pwd]/CTS/log "\nThe reply from user is : $retMessage" "a"
}
我发送一个新请求为“exec:Hello” 我发送回复为“回复:是”
正如您在proc svcHandler {}中看到的,如果它是一个新请求,我执行executeCommand {} proc。否则,我设置了replyReady标志。当我执行executeCommand {} proc时,调用proc runServerTest {},它位于serverTest.tcl中。现在在这个过程中,我将问题发回给用户,我可以在客户端的JAVA UI屏幕上看到。当我尝试发送回复时,svcHandler {}中的proc setReplyready {}根本不执行。当我看到日志文件时,我可以看到proc getReplyReadyStatus {}的输出,它在proc runServerTest {}中从while循环调用,每秒一次。这意味着while循环无限期地执行。并且由于没有从svcHandler {}调用proc setReplyReady {},因此while循环不会退出。真的不知道这个过程在哪里等待。任何解决方法?
谢谢, Peeyush
答案 0 :(得分:6)
不要使用线程。使用事件循环。
阅读fileevent
:http://www.tcl.tk/man/tcl8.6/TclCmd/fileevent.htm
通常,您为Java客户端套接字和ABC脚本套接字设置事件处理程序,以便在它们可读时触发,并在数据到达时执行您需要执行的操作。
如果需要实现定时操作,例如看门狗定时器,请查看after
命令。
答案 1 :(得分:3)
您想要异步通信,服务器和用户有多件事,但在Tcl版本小于8.6的情况下,每个解释器只能有1个执行点(堆栈帧)。因此,您不能等待一个循环中的服务器通信和另一个循环中的用户通信。要么将其中一个重写为执行它的工作并且返回的处理程序,要么使用:
答案 2 :(得分:3)
您不需要使用线程。相反,我建议使用具有coroutines的Tcl 8.6。
示例:
proc listfiles {} {
set lines 0
set res {}
foreach f [glob -tails -directory /foo/bar *] {
append res $f\n
if {[incr lines] >= 23} {
yield $res
set res {};
set lines 0
}
}
return $res
}
set corono 0
proc accept {sock host port} {
fconfigure $sock -blocking 0 -encoding utf-8 -buffering line
set coro ::coro[incr ::corono]
puts $sock [coroutine $coro listfiles]
if {[llength [info commands $coro]]} {
# still something to do
puts $sock "Do you want to continue ?"
fileevent $sock readable [list readsock $coro $sock]
} else {
close $sock
}
}
proc readsock {coro sock} {
# Tcl treats 1, yes, true and on as true. see http://www.tcl.tk/man/tcl8.6/TclLib/GetInt.htm
if {[gets $sock line] != -1 && [string is true $line]} {
fileevent $sock readable {}
puts $sock [$coro]
if {[llength [info commands $coro]]} {
# still something to do
puts $sock "Do you want to continue ?"
fileevent $sock readable [list readsock $coro $sock]
} else {
close $sock
}
} elseif {[string is false $line]} {
close $sock
}
}
socket -server accept 12457
套接字处理等的代码有点长,但实际计算很简单:如果已经收集了足够的数据,则调用yield,这将暂停当前执行。完成后,请致电return。
您应该添加某种错误处理。