Here is a small network server that illustrates
the sophisticated I/O and socket facilities in Tcl.
The server can accept connections from multiple clients.
For each client,
the server just echoes what a client sends it.
Tcl manages all the I/O with different clients so the
server does not have to block servicing a client or
waiting for connections.
You can use this server as the basis for almost any network service.
First we present the program and a summary of the commands in it.
After that we'll look at parts of the program in more detail.
(Make sure you've skimmed over the
Tcl Syntax first.)
Echo Service
Download
# Echo_Server --
# Open the server listening socket
# and enter the Tcl event loop
#
# Arguments:
# port The server's port number
proc Echo_Server {port} {
set s [socket -server EchoAccept $port]
vwait forever
}
# Echo_Accept --
# Accept a connection from a new client.
# This is called after a new socket connection
# has been created by Tcl.
#
# Arguments:
# sock The new socket connection to the client
# addr The client's IP address
# port The client's port number
proc EchoAccept {sock addr port} {
global echo
# Record the client's information
puts "Accept $sock from $addr port $port"
set echo(addr,$sock) [list $addr $port]
# Ensure that each "puts" by the server
# results in a network transmission
fconfigure $sock -buffering line
# Set up a callback for when the client sends data
fileevent $sock readable [list Echo $sock]
}
# Echo --
# This procedure is called when the server
# can read data from the client
#
# Arguments:
# sock The socket connection to the client
proc Echo {sock} {
global echo
# Check end of file or abnormal connection drop,
# then echo data back to the client.
if {[eof $sock] || [catch {gets $sock line}]} {
close $sock
puts "Close $echo(addr,$sock)"
unset echo(addr,$sock)
} else {
puts $sock $line
}
}
#
# A client of the echo service.
#
proc Echo_Client {host port} {
set s [socket $host $port]
fconfigure $s -buffering line
return $s
}
# A sample client session looks like this
# set s [Echo_Client localhost 2540]
# puts $s "Hello!"
# gets $s line
|
Command Summary
| socket -server command port |
Create a server listening socket.
Call command when clients connect. |
| vwait varname |
Enter the event loop and wait until the named
global variable is modified. |
| fileevent channel what command |
Register command to
be called when channel is ready for what sort of I/O:
readable or writable |
| fconfigure channel options |
Control the way file I/O is
done using channel |
| eof channel |
Test if channel is at end-of-file. |
| catch script varname |
Evaluate script and put
the result or the error result into varname.
Return the result code, which is 0 for success or 1 for failure. |
| puts string |
Write string to stdout, standard output |
| puts channel string |
Write string to channel |
| gets channel varname |
Read a line of input from
channel and store the result in the variable named
varname. The number of bytes read is returned. |
| close channel |
Close the I/O channel |
|
The Server Socket
A network server listens for connection requests from clients.
The operating system creates a new socket connection between each
client and the server,
so the server can have several clients connected simultaneously.
The Tcl socket -server command
associates the server's listening socket with a callback
that Tcl invokes when clients make connections.
If you are familiar with the C-level socket APIs, Tcl
socket command
call hides all the details of the bind(), listen(), and accept()
system calls:
set s [socket -server EchoAccept $port]
In this case, EchoAccept is called each time a client connects
to the server.
For this to work, Tcl must enter the event loop so it can wait
for the connection requests. The
vwait command is used for this purpose.
It puts Tcl into the event loop until the variable is modified.
In this case, we never plan to modify the variable,
so we call it "forever".
What is the Event Loop?
The event loop is a facility built into the Tcl runtime.
It lets you register Tcl commands that are called later
when various events occur. Example events include
socket connections, data ready on an I/O channel,
timer events, and window system events like mouse motion
and keyboard presses.
There are two commands in the example that register event
callbacks: the socket -server command described above,
and the fileevent command described in the next section.
The event loop needs to be active for the callbacks to work.
If you use wish for a Tk application, then the event
loop is active automatically.
However, if you use tclsh then you must activate the
event loop explicitly. The vwait command enters the
event loop until a variable is modified. You can use it for
a short-term wait that is ended by setting the variable, or you
can wait indefinetly.
Client Connections
The EchoAccept command is called when a client
connects to the server. Tcl adds three arguments to the
command when it makes the callback:
the new socket for the connection, the client's
IP address, and the client's port.
In the example, the client's address and port are saved
in an array, and in general a server will keep some sort
of state about each client.
Line Buffering
The server puts the new socket into line-buffering mode so
that each puts results in a network transmission.
Otherwise Tcl buffers data until the buffer fills or a
flush command is used.
The fconfigure command sets up the buffering mode:
fconfigure $sock -buffering line
Event-Driven I/O
The EchoAccept procedure
sets up another callback to handle
the data from the client on the new connection:
fileevent $sock readable [list Echo $sock]
The Echo command is called when the server can read the socket.
With fileevent the callback is a complete command that explicitly
includes the new socket.
The list command is used to create a well-formed
Tcl command for the callback.
(For more about list and eval, read
about Quoting
Issues and Eval)
End-of-File and Errors
The Echo command checks for end-of-file:
eof $sock
Then it reads a line from the socket and catches error
that might occur:
catch {gets file line}
The first argument to catch is a script.
Tcl will put the script through the normal evaluation cycle,
so you can brace the whole script in curly braces.
This example will run the gets command to read a line of input.
That command could raise an error if the socket is in
certain error states, so we put it inside a catch.
Finally, the server replies simply by writing the line back to the client:
puts $sock $line
The Client Side
The client is very simple:
set s [socket $host $port]
fconfigure $s -buffering line
The first command opens the TCP socket connection to the server
(which triggers its call to EchoAccept).
The second command sets up line buffering so each
puts command results in a network transmission.
As an experiment, you can also try using the Unix telnet
program to connect to your server.
Most telnets take the port number as the second argument:
telnet hostname port