Channel I/O: socket, fileevent, vwait
Tcl I/O is based on a 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 lesson 26. A socket
based channel is created with a 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 tcl 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.
Note in particular the flush
commands being used. Just as a channel that is opened as a pipe to
a command doesn't send data until either a flush
is invoked, or a buffer is filled, the
socket based channels don't automatically send data.
socket -server
command
?options?
port
- The
socket
command with the-server
flag starts a server socket listing on portport
. When a connection occurs onport
, the proccommand
is called with the arguments:channel
- The channel for the new clientaddress
- The IP Address of this clientport
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 Addresshost
and port addressport
. 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
readable
?script?
fileevent
channelID
writeable
?script?
- The
fileevent
command defines a handler to be invoked when a condition occurs. The conditions arereadable
, which invokesscript
when data is ready to be read onchannelID
, andwriteable
, whenchannelID
is ready to receive data. Note that end-of-file must be checked for by thescript
. vwait
varName
- The
vwait
command pauses the execution of a script until some background action sets the value ofvarName
. A background action can be a proc invoked by a fileevent, or a socket connection, or an event from a tk widget.
Examples
proc serverOpen {channel addr port} { global connected set connected 1 fileevent $channel readable "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 } } 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