multiplexer.tcl

Go to the documentation of this file.
00001 /*  multiplexer.tcl -- one-to-many comunication with sockets*/
00002 /* */
00003 /*  Implementation of a one-to-many multiplexer in Tcl utilizing*/
00004 /*  sockets.*/
00005 
00006 /*  Copyright (c) 2001-2003 by David N. Welton <davidw@dedasys.com>*/
00007 
00008 /*  This file may be distributed under the same terms as Tcl.*/
00009 
00010 /*  $Id: multiplexer.tcl,v 1.4 2004/01/15 06:36:13 andreas_kupries Exp $*/
00011 
00012 package provide multiplexer 0.2
00013 package require logger
00014 
00015 namespace ::multiplexer {
00016     variable Unique 0
00017 }
00018 
00019 ret  ::multiplexer::create () {
00020     variable Unique
00021     set ns ::multiplexer::mp$Unique
00022 
00023     namespace eval $ns {
00024     # Use the namespace as the logger name.
00025     set log [logger::init [string trimleft [namespace current] ::]]
00026     # list of connected clients
00027     array set clients {}
00028 
00029     # filters to run at access (socket accept) time
00030     set accessfilters {}
00031 
00032     # filters to run on data
00033     set filters {}
00034 
00035     # hook to run at exit time
00036     set exitfilters {}
00037 
00038     # config options
00039     array set config {}
00040     set config(sendtoorigin) 0
00041     set config(debuglevel) warn
00042     ${log}::disable $config(debuglevel)
00043     ${log}::enable $config(debuglevel)
00044 
00045     # AddAccessFilter --
00046     #
00047     # Command to add an access filter that will be called like so:
00048     #
00049     # AccessFilter chan clientaddress clientport
00050     #
00051     # Arguments:
00052     #
00053     # function: proc to filter access to the multiplexer.  Takes chan,
00054     # clientaddress and clientport arguments.  Returns 0 on success, -1 on
00055     # failure.
00056 
00057     proc AddAccessFilter { function } {
00058         variable accessfilters
00059         lappend accessfilters $function
00060     }
00061 
00062     # AddFilter --
00063 
00064     # Command to add a filter for data that passes through the
00065     # multiplexer.  The filter proc is called like this:
00066 
00067     # Filter data chan clientaddress clientport
00068 
00069     # Arguments:
00070 
00071     # function: proc to filter data that arrives to the
00072     # multiplexer.
00073     # Takes data, chan, clientaddress, and clientport arguments.  Returns
00074     # filtered version of data.
00075 
00076     proc AddFilter { function } {
00077         variable filters
00078         lappend filters $function
00079     }
00080 
00081     # AddExitFilter --
00082 
00083     # Adds filter to be run when client socket generates an EOF condition.
00084     # ExitFilter functions look like the following:
00085 
00086     # ExitFilter chan clientaddress clientport
00087 
00088     # Arguments:
00089 
00090     # function: hook to be run when clients exit by generating an EOF.
00091     # Takes chan, clientaddress and clientport arguments, and returns
00092     # nothing.
00093 
00094     proc AddExitFilter { function } {
00095         variable exitfilters
00096         lappend exitfilters $function
00097     }
00098 
00099     # DelClient --
00100 
00101     # Deletes a client from the client list, and runs exit filters.
00102 
00103     # Arguments:
00104 
00105     # chan: channel that is closed.
00106 
00107     # client: address of client
00108 
00109     # clientport: port number of client.
00110 
00111     proc DelClient { chan client clientport } {
00112         variable clients
00113         variable exitfilters
00114         variable config
00115         variable log
00116         foreach ef $exitfilters {
00117         catch {
00118             $ef $chan $client $clientport
00119         } err
00120         ${log}::debug "Error in DelClient: $err"
00121         }
00122         unset clients($chan)
00123         close $chan
00124     }
00125 
00126 
00127     # MultiPlex --
00128 
00129     # Multiplex data
00130 
00131     # Arguments:
00132 
00133     # data - data to multiplex
00134 
00135     proc MultiPlex { data {chan ""} } {
00136         variable clients
00137         variable config
00138         variable log
00139 
00140         foreach c [array names clients] {
00141         if { $config(sendtoorigin) } {
00142             puts -nonewline $c "$data"
00143         } else {
00144             if { $chan != $c } {
00145             ${log}::debug "Sending '$data' to $c"
00146             puts -nonewline $c "$data"
00147             }
00148         }
00149         }
00150     }
00151 
00152 
00153     # GetData --
00154 
00155     # Get data from clients, filter it, redistribute it.
00156 
00157     # Arguments:
00158 
00159     # chan: open channel
00160 
00161     # client: client address
00162 
00163     # clientport: port number of client
00164 
00165     proc GetData { chan client clientport } {
00166         variable filters
00167         variable clients
00168         variable config
00169         variable log
00170         if { ! [eof $chan] } {
00171         set data [read $chan]
00172     #   gets $chan data
00173         ${log}::debug "Tcl chan $chan from host $client and port $clientport sends: $data"
00174         # do data filters
00175         foreach f $filters {
00176             catch {
00177             set data [$f $data $chan $client $clientport]
00178             } err
00179             ${log}::debug "GetData filter: $err"
00180         }
00181         set chans [array names clients]
00182         MultiPlex $data $chan
00183         } else {
00184         ${log}::debug "Deleting client $chan from host $client and port $clientport."
00185         DelClient $chan $client $clientport
00186         }
00187     }
00188 
00189     # NewClient --
00190 
00191     # Sets up newly created connection after running access filters
00192 
00193     # Arguments:
00194 
00195     # chan: open channel
00196 
00197     # client: client address
00198 
00199     # clientport: port number of client
00200 
00201     proc NewClient { chan client clientport } {
00202         variable clients
00203         variable config
00204         variable accessfilters
00205         variable log
00206         # run through access filters
00207         foreach af $accessfilters {
00208         if { [$af $chan $client $clientport] == -1 } {
00209             ${log}::debug "Access denied to $chan $client $clientport"
00210             close $chan
00211             return
00212         }
00213         }
00214         set clients($chan) $client
00215 
00216         # We want to read data and immediately send it out again.
00217         fconfigure $chan -blocking 0
00218         fconfigure $chan -buffering none
00219         fconfigure $chan -translation binary
00220         fileevent $chan readable [list [namespace current]::GetData $chan $client $clientport]
00221         ${log}::debug "Tcl channel $chan is host $client and port $clientport."
00222     }
00223 
00224     # Config --
00225     #
00226     # Configure global options, which currently include the
00227     # following:
00228     #
00229     # sendtoorigin: if 1, resend the data to all clients, including the
00230     # sender.  Defaults to 0
00231     #
00232     # debuglevel: a debug level understood by logger.
00233     #
00234     # Arguments:
00235     #
00236     # key: name of option to configure
00237     #
00238     # value: value for option.
00239 
00240     proc Config { key value } {
00241         variable config
00242         variable log
00243         if { $key == "debuglevel" } {
00244         ${log}::disable $config(debuglevel)
00245         ${log}::enable $value
00246         }
00247         set config($key) $value
00248     }
00249 
00250     # Init --
00251     #
00252     # Start the server
00253     #
00254     # Arguments:
00255     #
00256     # port: port to listen on.
00257 
00258     proc Init { port } {
00259         variable serversock
00260         set serversock [socket -server [namespace current]::NewClient $port]
00261     }
00262 
00263     # destroy --
00264     #
00265     #   Destroy multiplexer instance.  It is important to do
00266     #   this, to free the resources used.
00267     #
00268     # Side Effects:
00269     #   Deletes namespace associated with multiplexer
00270     #   instance.
00271 
00272 
00273     proc destroy { } {
00274         variable serversock
00275         foreach c [array names clients] {
00276             catch { close $c }
00277         }
00278         catch {
00279         close $serversock
00280         }
00281         namespace delete [namespace current]
00282     }
00283 
00284     }
00285     incr Unique
00286     return $ns
00287 }
00288 
00289 namespace multiplexer {
00290     namespace export create destroy
00291 }
00292 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1