Search code examples
socketsserverclienttcl

Executing command on the server side


While trying to run a server-client based functionality using TCL, i am unable to execute the command tcl commands other than puts "string" when the client sends something to execute on the server side (both client and server are tcl based). I tried commands as below on the client:

puts $channel {set x 1};flush $channel;
puts $channel {incr x}; flush $channel;
puts $channel {puts $x}; flush $channel;

But the last line doesn't show the value of x, it just gives a blank line as an output. On the server side, i am using:

if {[catch {eval $line} $result]} {puts $result }

Can someone suggest why x is not being evaluated and printed? Also, how does scope is defined for variables sent from client to server or vise versa?


Solution

  • The third argument of the catch command is supposed to be a variable name. You used the value of the result variable, which apparently is empty, given that you report that the command produces a blank line as an output.

    So you have to at least omit the dollar sign:

    if {[catch {eval $line} result]} {puts $result}
    

    But given that catch will evaluate its second argument, that's a bit convoluted. It can be simplified to:

    if {[catch $line result]} {puts $result}
    

    When you handle the socket data inside a proc (that you invoke from e.g. a file event), the scope will be that proc. So every time you will get a (nearly) blank scope. The effects of the earlier commands that ran in a different invocation of the proc will have been lost. That's why the puts $x is likely to fail with can't read "x": no such variable.

    You can run the received commands in the global scope using:

    if {[catch {uplevel #0 $line} result]} {puts $result}
    

    It is likely even better to run them in a separate (safe) interpreter:

    # In your initialization code (running once):
    interp create rje
    
    # In your file event handler:
    if {[catch {rje eval $line} result]} {puts $result}
    

    When using a safe interpreter, it doesn't automatically have access to the "stdout" channel. You will have to arrange for that as follows:

    # In your initialization code (running once):
    interp create -safe rje
    interp share {} stdout rje
    
    # In your file event handler:
    if {[catch {rje eval $line} result]} {puts $result}