# ----------------------------------------------------------------------
#  FILE: echoserv2.tcl
#  DESCRIPTION: A simple example of a multi-threaded server.  Accept
#               a client connection, then echo back all lines sent by
#               the client until EOF or until receiving a line with
#               only the string "quit".
#
#               The server starts a separate thread for each client that
#               connects. Communication with the client in each thread
#               continues to be event-driven using fileevent handlers.
# ======================================================================
#              Copyright (c) 2000-2002 Kenneth Jones
# ======================================================================

package require Tcl 8.4
package require Thread 2.5

socket -server _ClientConnect 9001

proc _ClientConnect {sock host port} {
    
    # Tcl 8.4 holds a reference to the client socket during this
    # callback, so we can't transfer the channel to our worker
    # thread immediately. Instead, we'll schedule an after event
    # to transfer the channel once we've re-entered the event loop.

    after idle [list ClientConnect $sock $host $port]
}

proc ClientConnect {sock host port} {

    fconfigure $sock -buffering none -blocking 0

    set thread [thread::create {
                
        # Work around bug in Tcl's channel initialization when
        # transfering sockets between threads.
        
        close [socket -server {} 0]
        thread::wait
    }]
    
    thread::send $thread {
        
        proc ReadLine {sock} {
            if {[catch {gets $sock line} len] || [eof $sock]} {
                catch {close $sock}
                thread::release
            } elseif {$len >= 0} {
                EchoLine $sock $line
            }
        }

        proc EchoLine {sock line} {
            if {[string equal -nocase $line quit]} {
                SendMessage $sock "Closing connection to Echo server"
                catch {close $sock}
                thread::release
            } else {
                SendMessage $sock $line
            }
        }

        proc SendMessage {sock msg} {
            if {[catch {puts $sock $msg} error]} {
                puts "Error writing to socket: $error"
                catch {close $sock}
                thread::release
            }
        }
    }
    
    # Transfer the channel to the client's thread

    thread::transfer $thread $sock
    
    # Copy the value of the socket ID into the client's thread

    thread::send $thread [list set sock $sock]

    # Finish the socket setup

    thread::send $thread {
        fileevent $sock readable {ReadLine $sock}
    }
    
    # thread::send -async $thread {
    #     SendMessage $sock "Welcome to the Echo Server"
    #}
}

vwait forever
