我是否需要在TCL中使用Threads作为我的应用程序?

时间:2013-05-14 05:51:55

标签: multithreading sockets tcl

我正在尝试构建的应用程序如下。

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过程中等待,并根据用户输入从相同的忙等待中恢复处理。

标题## Server.tcl文件:

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

Heading ## serverTest.tcl(As ABC.tcl)

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

3 个答案:

答案 0 :(得分:6)

不要使用线程。使用事件循环。

阅读fileeventhttp://www.tcl.tk/man/tcl8.6/TclCmd/fileevent.htm

通常,您为Java客户端套接字和ABC脚本套接字设置事件处理程序,以便在它们可读时触发,并在数据到达时执行您需要执行的操作。

如果需要实现定时操作,例如看门狗定时器,请查看after命令。

答案 1 :(得分:3)

您想要异步通信,服务器和用户有多件事,但在Tcl版本小于8.6的情况下,每个解释器只能有1个执行点(堆栈帧)。因此,您不能等待一个循环中的服务器通信和另一个循环中的用户通信。要么将其中一个重写为执行它的工作并且返回的处理程序,要么使用:

  • Corclines in Tcl 8.6。
  • 多名口译员。
  • 多个进程(用它自己的tclsh启动ABC.tcl)。
  • 多线程。

One interpreter/thread per connection?

答案 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

您应该添加某种错误处理。