**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%|%]'''
!!!!!!