I (FW) created a little (~4k) chatserver in Tcl, which allows people to connect via a telnet client, or some other raw text-based program, and chat with others. People are assigned a unique name (the word "Unnamed" followed by an integer) and may subsequently rename themselves to their liking. Features included are a couple of different methods of communicating - the "say" command, which just sends a standard message, and the "me" command, which is roughly equivilant to the /me command in IRC (the one character shortcuts '"' and ':' are available for these, respectively); the "who" command, which lists the people online; the "name" command, which lets you reset your name; and the "help" command, which simply spits out a summary of the aforementioned commands.
I fancy this code so lucid it's almost self-documenting (thank the language for that, not me) and it serves to show a lot of the elegance of Tcl.
Update: Fixed a small bug with the : shortcut to the "me" command.
You might want to have a look at http://www.dedasys.com/freesoftware/ where you can find tclchat, which has both web and tk interfaces.
#!/bin/sh # the next line restarts using tclsh \ exec tclsh "$0" ${1+"$@"} set port 1234 proc handle_connection {client_socket host port} { global client_sockets counter name2socket socket2name set client_name "Unnamed$counter" incr counter announce "* $client_name has connected." lappend client_sockets $client_socket set name2socket($client_name) $client_socket set socket2name($client_socket) $client_name fconfigure $client_socket -buffering line fileevent $client_socket readable [list receive_line_from $client_socket] puts $client_socket "You are logged in as \"$client_name\". Type \"help\" to see a rundown of commands." } proc receive_line_from {client_socket} { global client_sockets name2socket socket2name set client_name $socket2name($client_socket) if {[catch {gets $client_socket line} send_error]} { clean_up_client $client_socket announce "* $client_name has disconnected \[Error: $error\]." return } elseif {[eof $client_socket]} { clean_up_client $client_socket announce "* $client_name has disconnected \[Connection closed by client\]." return } if {$line == ""} { puts $client_socket "You must enter some command." return } set first_character [string index $line 0] set rest [string range $line 1 end] if {$first_character == "\""} { set command_name "say" set command_data $rest } elseif {$first_character == ":"} { set command_name "me" set command_data $rest } elseif {![regexp {^(.+?) (.*)} $line dummy command_name command_data]} { # If the above line fails to find a command with supplied data, then set the # command name to the whole string and the data to an empty string set command_name $line set command_data "" } switch -- $command_name { say { announce "$client_name says, \"$command_data\"" } me { announce "$client_name $command_data" } who { puts $client_socket "The following people are online:" puts $client_socket "------------" foreach wsocket $client_sockets { puts $client_socket $socket2name($wsocket) } puts $client_socket "------------" } name { set new_name $command_data if {$new_name == $client_name} { puts $client_socket "You already are using that name." } elseif {[string is word $new_name] && [string length $new_name] <= 20} { foreach wsocket $client_sockets { if {$socket2name($wsocket) == $new_name} { puts $client_socket "That name is already in use." return } } set socket2name($client_socket) $new_name unset name2socket($client_name) set name2socket($new_name) $client_socket announce "* $client_name is now known as $new_name." } else { puts $client_socket "You must pick a name which is at most 20 characters long and which consists of only alphanumeric characters and underscores." } } help { puts $client_socket "Command rundown:" puts $client_socket " say Hello (or) \"Hello" puts $client_socket " me waves (or) :waves" puts $client_socket " who" puts $client_socket " name New_Name" puts $client_socket " help" } default { puts $client_socket "Invalid command." } } } proc announce {message} { global client_sockets foreach client_socket $client_sockets { puts $client_socket $message } } proc clean_up_client {client_socket} { global name2socket socket2name client_sockets close $client_socket set pos [lsearch -exact $client_sockets $client_socket] set client_sockets [lreplace $client_sockets $pos $pos] unset name2socket($socket2name($client_socket)) socket2name($client_socket) } set client_sockets [list] array set name2socket [list] array set socket2name [list] set counter 1 if {[catch {socket -server handle_connection $port} listen_error]} { puts "Failed to listen for connections on $port: $listen_error" } else { puts "Server started on port $port!" vwait forever }