Version 6 of client/server with fileevent

Updated 2007-06-19 09:24:34 by cjl

This is an example how to use fileevent.

 # usage: tclsh clisrv.tcl [secs]
 # Andy Tannenbaum, June 2001

 # all lines prepended with one space, for the wiki.

 # a parent/client and child/server both in this file,
 # communicating with fileevent.
 # should run on tcl 7.5 and later.

 # the two sides share names of things, but they will
 # never be invoked in the same address space.

 # the child ticks every second,
 # and accepts commands from the parent.

 # if invoked with no command line args, run parent.
 # with args, run child.

 if [string match "" $argv] {

 # parent/client process
 # calls clisrv.tcl with args to invoke child/server
 # opening a read/write pipe
 # using fileevent.
 #


 # done gets set when the child exits.
 # cfd is the child file descriptor

 global done cfd

 # gotline is the fileevent callback,
 # called when this proc receives input.

 proc gotline f {
        global done

        if {[gets $f line]<0} {
                catch {close $f} ret
                if ![string match "" $ret] {
                        puts "parent: gotline: child exited with \
                                error, ret = $ret, errorCode = $::errorCode"
                } else {
                        puts "parent: gotline: child exited ok"
                }
                set done 1
                return
        }

        puts "parent: got ==> $line"
 }

 proc fputs {f str} {
        puts $f $str
        flush $f
 }

 # prints:

 # parent: got ==> tick: 10
 # parent: got ==> tick: 9
 # parent: got ==> child: got ==> hello 1
 # parent: got ==> tick: 8
 # parent: got ==> child: got ==> hello 2
 # parent: got ==> tick: 7
 # parent: got ==> child: got ==> hello 3
 # parent: got ==> tick: 6
 # parent: got ==> tick: 5
 # parent: got ==> tick: 4
 # parent: got ==> child: got quit - q
 # parent: gotline: child exited ok

 proc pa {} {
        global cfd

        # run this script with arg, tick for 10 seconds
        set cfd [open "|tclsh [info script] 10 2>@ stderr" r+]
        fileevent $cfd readable "gotline $cfd"
 }

 # send commands to child process.
 # q means quit, others get echoed back to parent.

 after 1500 {
        global cfd
        fputs $cfd "hello 1"
 }

 after 2500 {
        global cfd
        fputs $cfd "hello 2"
 }

 after 3500 {
        global cfd
        fputs $cfd "hello 3"
 }

 after 6500 {
        global cfd
        fputs $cfd q
 }

 pa

 # vwait gives us an event loop
 # it returns when the child exits

 vwait done



 # end of parent/client
 # <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>

 } else {

 # <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
 # beginning of child/server



 # if invoked with args.
 # child process, ticks every second.
 # waits for stdin with fileevent.
 # echoes stdin back to stdout.


 # done gets set when the parent exits

 global done

 # gotline gets called on each line of input

 proc gotline f {
        global done
        if {[gets $f line]<0} {
                # it died !
                catch {close $f}
                set done 1
                return
        }

        # if parent sends quit, then quit.
        if [string match q* $line] {
                puts "child: got quit - $line"
                set done 1
                return
        }
        # else echo the input
        puts "child: got ==> $line"
 }

 fileevent stdin readable "gotline stdin"

 # print a tick every second, for n seconds, counting down.

 proc tick {n} {
        global done
        if {$n <= 0} {
                set done 1
                return
        }
        puts "tick: $n"
        incr n -1
        after 1000 tick $n
 }

 set arg1 [lindex $argv 0]

 tick $arg1
 vwait done
 }

Andrew Tannenbaum

Also see "Simple Server/Client Sockets" and telnet.