Welcome, guest | Sign In | My Account | Store | Cart

Here we write the most simple socket based server application we can think of. Starting from this minimal example it is very easy to extend it to the actually wanted functionality

Tcl, 32 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/local/bin/tcl
# fileevent/socket example

# Change the port to meet requirements.  Read it for example from the
# commandline or a configuration file

socket -server on_connect 18018

# Procedure called whenever a new connection is made by a client.
proc on_connect {newsock clientAddress clientPort} {

    # This is the place to add checks disallowing connections based
    # upon the hostname/ipaddress of the peer.

    fconfigure $newsock -blocking 0
    fileevent  $newsock readable [list handleInput $newsock]
}

# Procedure called whenever input arrives on a connection.
proc handleInput {f} {
    # Delete the handler if the input was exhausted.
    if {[eof $f]} {
        fileevent $f readable {}
        close     $f
        return
    }

    # Read and handle the incoming information. Here we just log it to
    # stdout.

    puts [read $f]
}

When it comes to the handling of the data read by the server on the connections developers have quite many ways to do so. Under the assumption of a line-oriented protocol where the first word in a line contains the command sent by the client we have:

Use a [switch] statement to decode the protocol command and to dispatch to the procedure implementing it.

Use an array to map from the protocol command to the procedure implementing it, then use [eval] to execute it. UUnknown and thus illegal commands can be detected by using [info exists] to check whether there actually is a mapping or not.

Construct the name of the procedure directly from the protocol command and then [eval] it. Use [info command] to check the existence of the procedure before doing so.

The simplest way of doing the construction is to use the name of the protocol command itself as the name of the procedure to execute. Note that this method is extremely unsafe as the client may send arbitrary tcl commands. This insecurity is commonly avoided through the use of a safe interpreter containing just the legal commands and thus providing a sandbox the client cannot leave.

Another thing to note: When reading data from a socket we should always be aware that it can arrive choppeed up into blocks of arbitrary length without relation to the blocks which were send. TCP guarantees arrival of bytes in order, but nothing more.

This means that in the case of line-oriented we should always use<pre> if {[gets $sock line] < 0} {return}</pre> to detect and handle incomplete lines.

In the case of non-line-oriented (and especially binary) protocols and [read] we should always read all which is available and then buffer this up until we have complete information we need at a particular step. <pre> append buffer [read $sock] if {[data-complete buffer]} { # Return the data we need and leave everything we didn't need in the buffer. set data [get-data buffer] handle $data } </pre> For a more complete example of a server take a look at the FTP server provided as part of tcllib ( http://sourceforge.net/projects/tcllib ).

Created by Jeff Hobbs on Thu, 21 Jun 2001 (MIT)
Tcl recipes (162)
Jeff Hobbs's recipes (16)

Required Modules

  • (none specified)

Other Information and Tasks