When a channel is closed for writing, any buffered output on the channel is flushed. When a channel is closed for reading, any buffered input is discarded. When a channel is destroyed the underlying resource is closed and the channel is thereafter unavailable.
chan close fully flushes any output before closing the write side of a channel unless it is non-blocking mode, where it returns immediately and the channel is flushed in the background before finally being closed.
chan close may return an error if an error occurs while flushing output. If a process in a command pipeline created by open returns an error (either by returning a non-zero exit code or writing to its standard error file descriptor), chan close generates an error in the same manner as exec.
Closing one side of a socket or command pipeline may lead to the shutdown() or close() of the underlying system resource, leading to a reaction from whatever is on the other side of the pipeline or socket.
If the channel for a command pipeline is in blocking mode, chan close waits for the connected processes to complete.
chan close only affects the current interpreter. If the channel is open in any other interpreter, its state is unchanged there. See interp for a description of channel sharing.
When the last interpreter sharing a channel is destroyed, the channel is switched to blocking mode and fully flushed and then closed.
Channels are automatically closed when an interpreter is destroyed and when the process exits. From 8.6 on (TIP#398), nonblocking channels are no longer switched to blocking mode when exiting; this guarantees a timely exit even when the peer or a communication channel is stalled. To ensure proper flushing of stalled nonblocking channels on exit, one must now either (a) actively switch them back to blocking or (b) use the environment variable TCL_FLUSH_NONBLOCKING_ON_EXIT, which when set and not equal to “0” restores the previous behavior.
If no optionName or value arguments are given, chan configure returns a dictionary of option names and values for the channel. If optionName is supplied without a value, chan configure returns the current value of the named option. If one or more pairs of optionName and value are supplied, chan configure sets each of the named options to the corresponding value and returns the empty string.
The options described below are supported for all channels. Each type of channel may provide additional options. Those options are described in the relevant documentation. For example, additional options are documented for socket, and also for serial devices at open.
binary is an alias for iso8859-1. This alone is not sufficient for working with binary data. Use -translation binary instead.
The encoding of a new channel is the value of encoding system, which returns the platform- and locale-dependent system encoding used to interface with the operating system,
The default value is the empty string. The acceptable range is \x01 - \x7F. A value outside this range results in an error.
Returns the input translation for a read-only channel, the output translation for a write-only channel, and both the input translation and the output translation for a read-write channel. When two translations are given, they are the input and output translation, respectively. When only one translation is given for a read-write channel, it is the translation for both input and output. The following values are currently supported:
-size limits the number of characters copied.
If -command is given, chan copy returns immediately, works in the background, and calls callback when the copy completes, providing as an additional argument the number of characters written to outputChan. If an error occurs during the background copy, another argument provides message for the error. inputChan and outputChan are automatically configured for non-blocking mode if needed. Background copying only works correctly if events are being processed, e.g. via vwait or Tk.
During a background copy no other read operation may be performed on inputChan, and no write operation may be performed on outputChan. However, write operations may by performed on inputChan and read operations may be performed on outputChan, as exhibited by the bidirectional copy example below.
If either inputChan or outputChan is closed while the copy is in progress, copying ceases and no callback is made. If inputChan is closed all data already queued is written to outputChan.
There should be no event handler established for inputChan because it may become readable during a background copy. An attempt to read or write from within an event handler results result in the error, "channel busy". Any wrong-sided I/O attempted (by a chan event handler or otherwise) results in a “channel busy” error.
Imode is a list of one or more of the strings “read” or “write”, indicating whether the channel is a read channel, a write channel, or both. It is an error if the handler does not support the chosen mode.
The handler is called as needed from the global namespace at the top level, and command resolution happens there at the time of the call. If the handler is renamed or deleted any subsequent attempt to call it is an error, which may not be able to describe the failure.
The handler is always called in the interpreter and thread it was created in, even if the channel was shared with or moved into a different interpreter in a different thread. This is achieved through event dispatch, so if the event loop is not entered, e.g. by calling Tcl_DoOneEvent or vwait or using Tk, the thread performing the channel operation blocks indefinitely, resulting in deadlock.
One side of a channel may be in one thread while the other side is in a different thread, providing a stream-oriented bridge between the threads. This provides a method for regular stream communication between threads as an alternative to sending commands.
When the interpreter the handler is in is deleted each channel associated with the handler is deleted as well, regardless of which interpreter or thread it is currently in or shared with.
chan create is safe and is accessible to safe interpreters. The handler is always called in the safe interpreter it was created in.
script is evaluated at the global level in the interpreter it was established in. Any resulting error is handled in the background, i.e. via interp bgerror. In order to prevent an endless loop due to a buggy handler, the handler is deleted if script returns an error so that it is not evaluated again.
Without an event handler, chan gets or chan read on a channel in blocking mode may block until data becomes available, during which the thread is unable to perform other work or respond to events on other channels. This could cause the application to appear to “freeze up” . Channel event handlers allow events on the channel to direct channel handling so that the reader or writer can continue to perform other processing while waiting for a channel to become available and then handle channel operations when the channel is ready for the operation.
A channel is considered to be readable if there is unread data available on the underlying device. A channel is also considered to be readable if there is unread data in an input buffer, except in the special case where the most recent attempt to read from the channel was a chan gets call that could not find a complete line in the input buffer. This feature allows a file to be read a line at a time in non-blocking mode using events. A channel is also considered to be readable if an end of file or error condition is present on the underlying file or device. It is important for script to check for these conditions and handle them appropriately; for example, if there is no special check for end of file, an infinite loop may occur where script reads no data, returns, and is immediately invoked again.
A channel is considered to be writable if at least one byte of data can be written to the underlying file or device without blocking, or if an error condition is present. Note that client sockets opened in asynchronous mode become writable when they become connected or if the connection fails.
Event-driven channel handling works best for channels in non-blocking mode. A channel in blocking mode blocks when chan puts writes more data than the channel can accept at the moment, and when chan gets or chan read requests more data than is currently available. When a channel blocks, the thread can not do any other processing or service any other events. A channel in non-blocking mode allows a thread to carry on with other work and get back to the channel at the right time.
If a complete line is not available and the channel is not at EOF, the command will block in the case of a blocking channel. For non-blocking channels, the command will return the empty string as the result in the case of varName not specified and -1 if it is.
If a blocking channel is already at EOF, the command returns an empty string if varName is not specified. Note an empty string result can also be returned when a blank line (no characters before the next end of line sequence). The two cases can be distinguished by calling the chan eof command to check for end of file. If varName is specified, the command returns -1 on end of file. There is no ambiguity in this case because blank lines result in 0 being returned.
If a non-blocking channel is already at EOF, the command returns an empty line if varName is not specified. This can be distinguished from an empty line being returned by either a blank line being read or a full line not being available through the use of the chan eof and chan blocked commands. If chan eof returns true, the channel is at EOF. If chan blocked returns true, a full line was not available. If both commands return false, an empty line was read. If varName was specified for a non-bocking channel at EOF, the command returns -1. This can be distinguished from full line not being available either by chan eof or chan blocked as above. Note that when varName is specified, there is no need to distinguish between eof and blank lines as the latter will result in the command returning 0.
If the encoding profile strict is in effect for the channel, the command will raise an exception with the POSIX error code EILSEQ if any encoding errors are encountered in the channel input data. The file pointer remains unchanged and it is possible to introspect, and in some cases recover, by changing the encoding in use. See ENCODING ERROR EXAMPLES later.
Due to buffering, data written to one side of a pipe might not immediately become available on the other side. Tcl's own buffers can be configured via chan configure -buffering, but overall behaviour still depends on operating system buffers outside of Tcl's control. Once the write side of the channel is closed, any data remaining in the buffers is flushed through to the read side. It may be useful to arrange for the connected process to flush at some point after writing to the channel or to have it use some system-provided mechanism to configure buffering. When two pipes are connected to the same process, one to send data to the process, and one to read data from the process, a deadlock may occur if the channels are in blocking mode: If reading, the channel may block waiting for data that can never come because buffers are only flushed on subsequent writes, and if writing, the channel may block while waiting for the buffers to become free, which can never happen because the reader can not read while the writer is blocking. To avoid this issue, either put the channels into non-blocking mode and use event handlers, or place the read channel and the write channel in separate interpreters in separate threads.
For use only by handlers for a channel created by chan create. It is an error to post an event for any other channel.
Since only the handler for a reflected channel channel should post events it is an error to post an event from any interpreter other than the interpreter that created the channel.
It is an error to post an event that the channel has no interest in. See watch in the refchan documentation for more information
chan postevent is available in safe interpreters, as any handler for a reflected channel would have been created, and will be evaluated in that interpreter as well.
Each line feed in the output is translated to the appropriate end of line sequence as per the -translation configuration setting of the channel.
Because Tcl internally buffers output, characters written to a channel may not immediately be available at the destination. Tcl normally delays output until the buffer is full or the channel is closed. chan flush forces output in the direction of the destination.
When the output for a channel in blocking mode fills up, chan puts blocks until space in the buffer is available again. On the other hand for a channel in non-blocking mode, it returns immediately and the data is written in the background as fast possible, constrained by the speed at which as the destination accepts it. Output to a channel in non-blocking mode only works properly when the application enters the event loop. When a channel is in non-blocking mode, Tcl's internal buffers can hold an arbitrary amount of data, possibly consuming a large amount of memory. To avoid wasting memory, channels in non-blocking mode should normally be handled using chan event, where the application only invokes chan puts after being notified through a file event handler that the channel is ready for more output data.
The command will raise an error exception with POSIX error code EILSEQ if the encoding profile strict is in effect for the channel and the output data cannot be encoded in the encoding configured for the channel. Data may be partially written to the channel in this case.
If the channel is in non-blocking mode, fewer characters than requested may be returned. If the channel is configured to use a multi-byte encoding, bytes that do not form a complete character are retained in the buffers until enough bytes to complete the character accumulate, or the end of the data is reached. -nonewline is ignored if characters are returned before reaching the end of the file.
Each end-of-line sequence according to the value of -translation is translated into a line feed.
When reading from a serial port, most applications should configure the serial port channel to be in non-blocking mode, but not necessarily use an event handler since most serial ports are comparatively slow. It is entirely possible to get a readable event for each individual character. In blocking mode, chan read blocks forever when reading to the end of the data if there is no chan configure -eofchar configured for the channel.
If the encoding profile strict is in effect for the channel, the command will raise an exception with the POSIX error code EILSEQ if any encoding errors are encountered in the channel input data. If the channel is in blocking mode, the error is thrown after advancing the file pointer to the beginning of the invalid data. The successfully decoded leading portion of the data prior to the error location is returned as the value of the -data key of the error option dictionary. If the channel is in non-blocking mode, the successfully decoded portion of data is returned by the command without an error exception being raised. A subsequent read will start at the invalid data and immediately raise a EILSEQ POSIX error exception. Unlike the blocking channel case, the -data key is not present in the error option dictionary. In the case of exception thrown due to encoding errors, it is possible to introspect, and in some cases recover, by changing the encoding in use. See ENCODING ERROR EXAMPLES later.
Chan seek flushes all buffered output even if the channel is in non-blocking mode, discards any buffered and unread input, and returns the empty string or an error if the channel does not support seeking.
offset values are byte offsets, not character offsets. Unlike chan read, both chan seek and chan tell operate in terms of bytes, not characters,
fconfigure stdout -buffering none
In the following example a file is opened using the encoding CP1252, which is common on Windows, searches for a string, rewrites that part, and truncates the file two lines later.
set f [open somefile.txt r+] chan configure $f -encoding cp1252 set offset 0 # Search for string "FOOBAR" in the file while {[chan gets $f line] >= 0} { set idx [string first FOOBAR $line] if {$idx >= 0} { # Found it; rewrite line chan seek $f [expr {$offset + $idx}] chan puts -nonewline $f BARFOO # Skip to end of following line, and truncate chan gets $f chan gets $f chan truncate $f # Stop searching the file now break } # Save offset of start of next line for later set offset [chan tell $f] } chan close $f
This example illustrates flushing of a channel. The user is prompted for some information. Because the standard input channel is line buffered, it must be flushed for the user to see the prompt.
chan puts -nonewline "Please type your name: " chan flush stdout chan gets stdin name chan puts "Hello there, $name!"
This example reads a file one line at a time and prints it out with the current line number attached to the start of each line.
set chan [open "some.file.txt"] set lineNumber 0 while {[chan gets $chan line] >= 0} { chan puts "[incr lineNumber]: $line" } chan close $chan
In this example illustrating event driven reads, GetData will be called with the channel as an argument whenever $chan becomes readable. The read call will read whatever binary data is currently available without blocking. Here the channel has the fileevent removed when an end of file occurs to avoid being continually called (see above). Alternatively the channel may be closed on this condition.
proc GetData {chan} { set data [chan read $chan] chan puts "[string length $data] $data" if {[chan eof $chan]} { chan event $chan readable {} } } chan configure $chan -blocking 0 -encoding binary chan event $chan readable [list GetData $chan]
The next example is similar but uses chan gets to read line-oriented data.
proc GetData {chan} { if {[chan gets $chan line] >= 0} { chan puts $line } if {[chan eof $chan]} { chan close $chan } } chan configure $chan -blocking 0 -buffering line -translation crlf chan event $chan readable [list GetData $chan]
A network server that echoes its input line-by-line without preventing servicing of other connections at the same time:
# This is a very simple logger... proc log message { chan puts stdout $message } # This is called whenever a new client connects to the server proc connect {chan host port} { set clientName [format <%s:%d> $host $port] log "connection from $clientName" chan configure $chan -blocking 0 -buffering line chan event $chan readable [list echoLine $chan $clientName] } # This is called whenever either at least one byte of input # data is available, or the channel was closed by the client. proc echoLine {chan clientName} { chan gets $chan line if {[chan eof $chan]} { log "finishing connection from $clientName" chan close $chan } elseif {![chan blocked $chan]} { # Didn't block waiting for end-of-line log "$clientName - $line" chan puts $chan $line } } # Create the server socket and enter the event-loop to wait # for incoming connections... socket -server connect 12345 vwait forever
The following example reads a PPM-format image from a file combining ASCII and binary content.
# Open the file and put it into Unix ASCII mode set f [open teapot.ppm] chan configure $f -encoding ascii -translation lf # Get the header if {[chan gets $f] ne "P6"} { error "not a raw-bits PPM" } # Read lines until we have got non-comment lines # that supply us with three decimal values. set words {} while {[llength $words] < 3} { chan gets $f line if {[string match "#*" $line]} continue lappend words {*}[join [scan $line %d%d%d]] } # Those words supply the size of the image and its # overall depth per channel. Assign to variables. lassign $words xSize ySize depth # Now switch to binary mode to pull in the data, # one byte per channel (red,green,blue) per pixel. chan configure $f -translation binary set numDataBytes [expr {3 * $xSize * $ySize}] set data [chan read $f $numDataBytes] close $f
set f [open file.txt] set data1 [chan read $f] chan seek $f 0 set data2 [chan read $f] chan close $f # $data1 eq $data2 if the file wasn't updated
Read the last 10 bytes from a file:
set f [open file.data] # This is guaranteed to work with binary data but # may fail with other encodings... chan configure $f -translation binary chan seek $f -10 end set data [chan read $f 10] chan close $f
Read a line from a file channel only if it starts with foobar:
# Save the offset in case we need to undo the read... set offset [tell $chan] if {[read $chan 6] eq "foobar"} { gets $chan line } else { set line {} # Undo the read... seek $chan $offset }
% set f [open test_A_195_B.txt wb]; chan puts -nonewline $f A\xC3B; chan close $f
An attempt to read the file will result in an encoding error which is then introspected by switching the channel to binary mode. Note in the example that when the error is reported the file position remains unchanged so that the chan gets during recovery returns the full line.
% set f [open test_A_195_B.txt r] file384b6a8 % chan configure $f -encoding utf-8 -profile strict % catch {chan gets $f} e d 1 % set d -code 1 -level 0 -errorstack {INNER {invokeStk1 gets file384b6a8}} -errorcode {POSIX EILSEQ {invalid or incomplete multibyte or wide character}} -errorinfo {...} -errorline 1 % chan tell $f 0 % chan configure $f -encoding binary -profile strict % chan gets $f AÃB
The following example is similar to the above but demonstrates recovery after a blocking read. The successfully decoded data "A" is returned in the error options dictionary key -data. The file position is advanced on the encoding error position 1. The data at the error position is thus recovered by the next chan read command.
% set f [open test_A_195_B.txt r] file35a65a0 % chan configure $f -encoding utf-8 -profile strict -blocking 1 % catch {chan read $f} e d 1 % set d -data A -code 1 -level 0 -errorstack {INNER {invokeStk1 read file35a65a0}} -errorcode {POSIX EILSEQ {invalid or incomplete multibyte or wide character}} -errorinfo {...} -errorline 1 % chan tell $f 1 % chan configure $f -encoding binary -profile strict % chan read $f ÃB % chan close $f
Finally the same example, but this time with a non-blocking channel.
% set f [open test_A_195_B.txt r] file35a65a0 % chan configure $f -encoding utf-8 -profile strict -blocking 0 % chan read $f A % chan tell $f 1 % catch {chan read $f} e d 1 % set d -code 1 -level 0 -errorstack {INNER {invokeStk1 read file384b228}} -errorcode {POSIX EILSEQ {invalid or incomplete multibyte or wide character}} -errorinfo {...} -errorline 1
chan configure $in -translation binary chan configure $out -translation binary chan copy $in $out
This second example shows how the callback gets passed the number of bytes transferred. It also uses vwait to put the application into the event loop. Of course, this simplified example could be done without the command callback.
proc Cleanup {in out bytes {error {}}} { global total set total $bytes chan close $in chan close $out if {$error ne ""} { # error occurred during the copy } } set in [open $file1] set out [socket $server $port] chan copy $in $out -command [list Cleanup $in $out] vwait total
The third example copies in chunks and tests for end of file in the command callback.
proc CopyMore {in out chunk bytes {error {}}} { global total done incr total $bytes if {($error ne "") || [chan eof $in]} { set done $total chan close $in chan close $out } else { chan copy $in $out -size $chunk \ -command [list CopyMore $in $out $chunk] } } set in [open $file1] set out [socket $server $port] set chunk 1024 set total 0 chan copy $in $out -size $chunk \ -command [list CopyMore $in $out $chunk] vwait done
The fourth example starts an asynchronous, bidirectional copy between two sockets. Those could also be pipes from two bidirectional pipelines (e.g., [open "|hal 9000" r+]); the conversation will remain essentially secret to the script, since all four chan event slots are busy, though any transforms that are chan pushed on the channels will be able to observe the passing traffic.
proc Done {dir args} { global flows done chan puts "$dir is over." incr flows -1 if {$flows <= 0} { set done 1 } } set flows 2 chan copy $sok1 $sok2 -command [list Done UP] chan copy $sok2 $sok1 -command [list Done DOWN] vwait done