|
Description:
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
Source: Text Source
socket -server on_connect 18018
proc on_connect {newsock clientAddress clientPort} {
fconfigure $newsock -blocking 0
fileevent $newsock readable [list handleInput $newsock]
}
proc handleInput {f} {
if {[eof $f]} {
fileevent $f readable {}
close $f
return
}
puts [read $f]
}
The license for this recipe is available here.
Discussion:
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
if {[gets $sock line] < 0} {return}
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.
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
}
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 ).
|