Previous section   Next section

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 17.  Socket Programming

The Echo Service

Example 17-3 The echo service.
proc Echo_Server {port} {
   global echo
   set echo(main) [socket -server EchoAccept $port]
proc EchoAccept {sock addr port} {
   global echo
   puts "Accept $sock from $addr port $port"
   set echo(addr,$sock) [list $addr $port]
   fconfigure $sock -buffering line
   fileevent $sock readable [list Echo $sock]
proc Echo {sock} {
   global echo
   if {[eof $sock] || [catch {gets $sock line}]} {
      # end of file or abnormal connection drop
      close $sock
      puts "Close $echo(addr,$sock)"
      unset echo(addr,$sock)
   } else {
      if {[string compare $line "quit"] == 0} {
         # Prevent new connections.
         # Existing connections stay open.
         close $echo(main)
      puts $sock $line

The echo server accepts connections from clients. It reads data from the clients and writes that data back. The example uses fileevent to wait for data from the client, and it uses fconfigure to adjust the buffering behavior of the network socket. You can use Example 17-3 as a template for more interesting services.

The Echo_Server procedure opens the socket and saves the result in echo(main). When this socket is closed later, the server stops accepting new connections but existing connections won't be affected. If you want to experiment with this server, start it and wait for connections like this:

Echo_Server 2540
vwait forever

The EchoAccept procedure uses the fconfigure command to set up line buffering. This means that each puts by the server results in a network transmission to the client. The importance of this will be described in more detail later. A complete description of the fconfigure command is given in Chapter 16. The EchoAccept procedure uses the fileevent command to register a procedure that handles I/O on the socket. In this example, the Echo procedure will be called whenever the socket is readable. Note that it is not necessary to put the socket into nonblocking mode when using the fileevent callback. The effects of nonblocking mode are discussed on page 221.

EchoAccept saves information about each client in the echo array. This is used only to print out a message when a client closes its connection. In a more sophisticated server, however, you may need to keep more interesting state about each client. The name of the socket provides a convenient handle on the client. In this case, it is used as part of the array index.

The Echo procedure first checks to see whether the socket has been closed by the client or there is an error when reading the socket. The if expression only performs the gets if the eof does not return true:

if {[eof $sock] || [catch {gets $sock line}]} {

Closing the socket automatically clears the fileevent registration. If you forget to close the socket upon the end of file condition, the Tcl event loop will invoke your callback repeatedly. It is important to close it when you detect end of file.

Example 17-4 A client of the echo service.
proc Echo_Client {host port} {
   set s [socket $host $port]
   fconfigure $s -buffering line
   return $s
set s [Echo_Client localhost 2540]
puts $s "Hello!"
gets $s
=> Hello!

In the normal case, the server simply reads a line with gets and then writes it back to the client with puts. If the line is "quit," then the server closes its main socket. This prevents any more connections by new clients, but it doesn't affect any clients that are already connected.

Example 17-4 shows a sample client of the Echo service. The main point is to ensure that the socket is line buffered so that each puts by the client results in a network transmission. (Or, more precisely, each newline character results in a network transmission.) If you forget to set line buffering with fconfigure, the client's gets command will probably hang because the server will not get any data; it will be stuck in buffers on the client.

      Previous section   Next section