A little client-server example

Summary

Richard Suchenwirth 2006-02-28: The following very simple script can act as either the server or the client of a socket connection:

  • When called with "server" as first argument, it listens on the given port. Lines coming in are evaluated, and the result is sent back
  • Otherwise, the script is a client: takes lines from stdin, sends them to the server, displays the result

Description

set host localhost
set port 9900
 
if {[lindex $argv 0] eq "server"} {
    puts "Server started..."
    socket -server server $port
} else {
    set chan [socket $host $port]
    fconfigure $chan -buffering line
    fileevent $chan readable [list client'read $chan]
    fileevent stdin readable [list client'send $chan]
}
#-----------------------------------------------------
proc server {chan addr port} {
    fconfigure $chan -buffering line ;# NOT -blocking 0 (see below!)
    while {[gets $chan line]>=0} {
        catch $line res
        puts $line->$res ;# local logging
        puts $chan $res
    }
    close $chan
}
#------------------------------------------------------
proc client'read chan {
    if {[eof $chan]} {close $chan; exit}
    gets $chan line
    puts <-$line
}
proc client'send chan {
    gets stdin line
    puts $chan $line
}
#------------------------------------------------------
vwait forever

RS 2008-09-26: Years (and possibly some "security" patches) later, this code runs no more on Win XP - the server starts up and waits, but clients get an EOF before they can even send something. Does anyone have an idea what could lead to this changed behavior?

MS: seems to recall that XP's firewall also blocks connections to localhost.

RS 2008-10-02: No, it was something different.. someone edited -blocking 0 into the server fconfigure. Taking that out again, it now works like it always did, at least on Windows XP :^)

Lars H: That means it operates in a rather unusual fashion, though. The server can only serve one connection at a time, since it sits in the server while loop rather than the event loop. How about

proc server {chan addr port} {
    fconfigure $chan -buffering line -blocking 0
    fileevent $chan readable [list server_eval $chan]
}
proc server_eval {chan} {
    if {![eof $chan]} then {
        gets $chan line
        catch $line res
        puts $line->$res ;# local logging
        puts $chan $res
    } else {
        close $chan
    }
}

?

See Also

A Server Template
client/server with fileevent
Network server application template
Simple Server/Client Sockets
The simplest possible socket demonstration
Concurreny in Tcl: Weaving Threads ,Andreas Kupries ,2010-05-10
A Thread-based Network Server