Tcl Tutorial Lesson 40

Difference between version 3 and 4 - Previous - Next

**Channel I/O: socket, fileevent, vwait**


!!!!!!
'''[Tcl Tutorial Lesson 26%|%Previous lesson%|%]''' | '''[Tcl Tutorial Index%|%Index%|%]''' | '''[Tcl Tutorial Lesson 42%|%Next lesson%|%]'''
!!!!!!



Tcl I/O is based on the concept of channels.  A channel is conceptually similar to a `FILE *` in C, or a stream in shell programming.  The difference is that a channel may be a either a stream device, like a file, or a connection -oriented construct, like a socket.

A stream -based channel is created with the `open` command, as discussed in [Tcl Tutorial Lesson 26%|%lesson 26%|%]. A socket -based channel is created with athe `socket` command. A socket can be opened as either as a client, or as a server.
If a socket channel is opened as a server, then the Tcl program will 'listen' on that channel for another task to attempt to connect with it.  When this happens, a new channel is created for that link (server -> new client), and the tTcl program continues to listen for connections on the original port number. In this way, a single Tcl server could be talking to several clients simultaneously.

When a channel exists, a handler can be defined that will be invoked when the channel is available for reading or writing. This handler is defined with the `fileevent` command. When a Tcl procedure does a `gets` or `puts` to a blocking device, and the device isn't ready for I/O, the program will block until the device is ready.  This may be a long while if the other end of the I/O channel has gone off line.  Using the `fileevent` command, the program only accesses an I/O channel when it is ready to move data.

Finally, there is a command to wait until an event happens.  The `vwait` command will wait until a variable is set.  This can be used to create a semaphore style functionality for the interaction between client and server, and let a controlling procedure know that an event has occurred.

Look at the example, and you'll see the `socket` command being used as both client and server, and the `fileevent` and `vwait` commands being used to control the I/O between the client and server.

   `socket -server command ?options? port`:   The `socket` command with the `-server` flag starts a server socket listing on port `port`.  When a connection occurs on `port`, the proc `command` is called with the arguments:

   * `channel` - The channel for the new client
   * `address` - The IP Address of this client
   * `port` The port that is assigned to this client

   `socket ?options? host port`:   The `socket` command without the `-server` option opens a client connection to the system with IP Address `host` and port address `port`.  The IP Address may be given as a numeric string, or as a fully qualified domain address. To connect to the local host, use the address 127.0.0.1 (the loopback address).

   `fileevent channelID writeable ?script?`:   The `fileevent` command defines a handler to be invoked when a condition occurs.  The conditions are `readable`, which invokes `script` when data is ready to be read on `channelID`, and `writeable`, when `channelID` is ready to receive data.  Note that end-of-file must be checked for by the `script`.

   `vwait varName`:   The `vwait` command pauses the execution of a script until some background action sets the value of the global variable `varName`.  A background action can be a proc invoked by a fileevent, or a socket connection, or an event from a Tk widget.

''Note:'' Since Tcl 8.5 many of the I/O features are also available via the `chan` command.

----

***Example***

The code below sets up a simple "echo" server, sends it two strings - which are sent back - and then closes.
While this happens in the same process, you can use this technique to run the server in a separate process as well.
The link with the client will be via the socket and the port.

======
#
# Define two auxiliary procs
#
proc serverOpen {channel addr port} {
    global connected
    set connected 1
    fileevent $channel readable [list readLine Server $channel]
    puts "OPENED"
}

proc readLine {who channel} {
    global didRead
    if { [gets $channel line] < 0} {
        fileevent $channel readable {}
        after idle "close $channel;set out 1"
    } else {
        puts "READ LINE: $line"
        puts $channel "This is a return"
        flush $channel;
        set didRead 1
    }
}
======

The code to start the ''server'' and connect to it from a ''client'':

======
set connected 0
# catch {socket -server serverOpen 33000} server
set server [socket -server serverOpen 33000]

after 100 update

set sock [socket -async 127.0.0.1 33000]
vwait connected

puts $sock "A Test Line"
flush $sock
vwait didRead
set len [gets $sock line]
puts "Return line: $len -- $line"

catch {close $sock}
vwait out
close $server
======

<<discussion>> Resulting output
======none
OPENED
READ LINE: A Test Line
Return line: 16 -- This is a return
======
<<enddiscussion>>

!!!!!!
'''[Tcl Tutorial Lesson 26%|%Previous lesson%|%]''' | '''[Tcl Tutorial Index%|%Index%|%]''' | '''[Tcl Tutorial Lesson 42%|%Next lesson%|%]'''
!!!!!!