This page is dedicated to the asyncroneous socket connect, started by 'socket -async'. See the socket page for a general description.
It also serves as communication page for development and compares current TCL 8.5.15, TCL 8.6.1 and future versions.
Async connect got more complicated in TCL 8.6, as multiple destination IPs are internally supported (due to IPV6 or DNS lookup resulting in multiple IPs).
The typical use-case for background connect is to install a writable event to get notified about the connect. If there is an additional connect timeout, this is canceled by the writable connect.
Typical code:
proc Connected {h fromip toip} { # check connect succes or fail set error [fconfigure $h -error] if {$error ne ""} { catch {close $h} return } # disable writable event as it will come again and again if nothing written here fileevent $h writable "" # do something with the socket puts $h "HELO" # install readable event to process reply fileevent $h readable Receive } set h [socket -async $host $port] fileevent $h writable Connected set aid [after 10000 Timeout]
If there is no need to get notified on a succesfull connect, one may use a readable connect only. Attention, this did not work in Windows before 8.5.16:
proc Receive {h fromip toip} { # check connect succes or fail set error [fconfigure $h -error] if {$error ne ""} { catch {close $h} return } # get read data and process it if {[catch {gets $h} data]} { # read error catch {close $h} return } if {[eof $h]} { # other side disconnected catch {close $h} return } # now do something with the data... } set h [socket -async $host $port] fileevent $h readable Read # if a message is needed by the server after the connect, send it now non-blocking # It will be automatically sent when the connect succeeds fconfigure $h -blocking 0 puts $h "HELO" flush $h
A use case is to start multiple connect, do something else and then process the connect state, all in a linear program without event queue. An example is a test if multiple servers are alive.
Example program:
set h [socket -async $host $port] # do something else which needs time # check if failed. Start also next try of multiple IPs of $host set error [fconfigure $h -error] if {$error ne""} { # connect failed catch {close $h} return } # do something else which needs time # check if failed. Start also next try of multiple IPs of $host set error [fconfigure $h -error] if {$error ne""} { # connect failed catch {close $h} return } # nothing to do, so do the rest syncroneously # this blocks ! if {[catch { puts $h "HELO" set Data [gets $h] close $h } error] { # connect failed catch {close $h} }
This example requires the command 'fconfigure -connecting' which is contained in the proposed TIP. It gives the state if the connection process is still running. This allows to do the upper example without blocking commands.
Example program:
set h [socket -async $host $port] while {[fconfigure $h -connecting]} { # do something else which needs time } # connection process terminated - check if failed set error [fconfigure $h -error] if {$error ne ""} { # connect failed catch {close $h} return } # do something with the connected socket
'socket -async' host first does a syncroneous DNS lookup.
Then the connect is started as background process.
Version | Status |
---|---|
8.5.15 | ok |
8.5.16+ | ok |
8.6.1 unix | ok, requires event loop |
8.6.1 win | only first IP (broken) |
8.6.2+ | ok, requires event loop |
ideas | may be moved in own thread to not require event loop and not to pause between connect tries |
Starting the event loop allows in TCL8.6 to continue with the next try or to fail finally. It is not absolutely necessary, as all other socket commands also advance the connect process.
As a start point for all other commands: if a failed async connect socket is not closed after the first reported error, bad things like unreported errors etc. may happen.
Please close an async socket connect after the first reported error.
Fires when async connect terminates with success or error.
'fconfigure -error' may be used in the event procedure to check if the connect was succesful.
Version | Status |
---|---|
8.5.15 | ok, see bugs |
8.5.16+ | ok |
8.6.1 win | only first IP (broken) |
8.6.1 unix | ok |
8.6.2+ | ok |
Fires when async connect terminates with error.
On a succesful connect, it fires only, if there is data received.
'fconfigure -error' may be used in the event procedure to check if the connect was succesful.
Version | Status |
---|---|
8.5.15 unix | ok |
8.5.15 win | only works when also writable event installed, see bugs |
8.5.16+ | ok |
8.6.1 win | only first IP and only with writable event (broken) |
8.6.1 unix | ok |
8.6.2+ | ok |
Remark: a puts may be delayed to a following flush.
The async connect is terminated syncroneously.
On success, the operation is performed.
On connect failure, the error "socket is not connected" is returned. The reason for the connect failure may be investigated using fconfigure -error.
Version | Status |
---|---|
8.5.15 unix | ok. Instead of "socket is not connected", "broken pipe" may be reported. |
8.5.15 win | ok |
8.5.16+ unix | ok |
8.5.16+ | ok |
8.6.1 win | only first IP tested (broken). |
8.6.1 unix | ok. Instead of "socket is not connected", "broken pipe" may be reported. |
8.6.2+ | ok |
Remark: a puts may be delayed to a following flush.
The async connect state is checked or continued (next IP) in a non-blocking way.
Eventual pending flush is executed in the background automatically when the connection is established and the event queue is running.
Possible results:
Number | Condition | Action |
---|---|---|
NB1 | async connect still in progress | write operation is buffered and sheduled for background flush. Read operation returns empty string |
NB2 | async connect succeeded | operation is directly executed |
NB3 | async connect failed | Error "socket is not connected" is returned |
Implementation status:
Version | Status |
---|---|
8.5.15 unix | ok. Instead of "socket is not connected", "broken pipe" may be reported. |
8.5.15 win | ok |
8.5.16+ unix | ok. Instead of "socket is not connected", "broken pipe" may be reported. |
8.5.16+ win | ok |
8.6.1 win | only first IP (broken) |
8.6.1 unix | ok. Instead of "socket is not connected", "broken pipe" may be reported. |
8.6.2+ win | ok |
8.6.2+ unix | ? |
A close while connection is in progress or after a succesful connection should succeed.
A close after a failed connection succeeds.
If a background flush is pending (or already resulted in an error), an error may be shown.
Version | Status |
---|---|
8.5.15 | ok. Empty error message may appear. |
8.5.16+ unix | ok |
8.5.16+ win | ok |
8.6.1 | ok. Empty error message may appear. |
8.6.2+ | ok |
eof should be active:
Version | Status |
---|---|
8.5.15 | ok |
8.5.16+ | ok |
8.6.1 | ok |
8.6.2+ | ok |
Any fconfigure command on the socket continues the connect process.
Version | Status |
---|---|
8.6.1 win | no |
8.6.1 unix | no |
8.6.2+ | ok |
A final connect error should be returned by 'fconfigure -error'. No error should be flagged while connection is running.
Implementation status:
Version | Status |
---|---|
8.5.15 unix | ok. |
8.5.15 win | ok. Small bug: Failed socket connect error is reported indefinitely |
8.5.16+ unix | ok. |
8.5.16+ win | ok. Small bug: Failed socket connect error is reported indefinitely |
8.6.1 win | result of first tested IP (broken) |
8.6.1 unix | The errors of all tested IPs show temporarely up |
8.6.2+ | ok |
To fix the small bug, that a connect error is repeated indefinitively may introduce compatibility issues of programs which rely on that.
Example TCL 8.5:
# no server on 30001 % set h [socket -async localhost 30001] sock448 % read $h error reading "sock448": socket is not connected % fconfigure $h -error connection refused % fconfigure $h -error connection refused
and TCL 8.6.2+:
# no server on 30001 % set h [socket -async localhost 30001] sock0ab6b2 % read $h error reading "sock0ab7b2": socket is not connected % fconfigure $h -error connection refused % fconfigure $h -error %
My own IP of the socket connection. Returns list of IP, Name, Port.
Since IPV6, this value may change within connect tries from "127.0.0.1" to "::1". Also the port may change.
Implementation status:
Version | Status |
---|---|
8.5.15 win | returns "0.0.0.0 0.0.0.0 51063" |
8.5.16+ | ? |
8.6.1 win | returns ":: :: 51070" if first try is IPV6, otherwise an IP V4 address |
8.6.1 unix | ? |
8.6.2+ | ? |
Idea (see TIP): The connection process should not be reflected. A compatibility value or an error should returned while connecting.
If we reflect the connection process, we would have issues later, if we would push the connect process to an own thread.
The destination IP. Returns list of IP, Name, Port.
Since IPV6, this value may change within connect tries. Also the port may change (if auto).
Implementation status:
Version | Status |
---|---|
8.5.15 win | returns information of tried IP while connecting. Error if connection failed |
8.5.16+ | ? |
8.6.1 win | returns information of first tried IP. Error if first connect try failed |
8.6.1 unix | reflects connection process |
8.6.2+ | ? |
Idea (see TIP): The connection process should not be reflected. A compatibility value or an error should returned while connecting.
A discussion tip on the behaviour of those commands while connecting was drafted.
A new option '-connecting' should return 1 if connection is still in process.
Reason for that: there are use cases for socket -async without event loop.
Example: Within Web-Server verify multiple servers for connectivity. Open many sockets in the background, do some other calculations, check if connect terminated by "-connecting".
A possibility to return the full POSIX information of a background error was drafted.
Two possible solutions:
TCL8.6.1 only tries the first of eventual multiple IP addresses to connect. This may cause serious connect issues, specially with IPV6.
This is fixed in branch bug-13d3af3ad5 which also serves as main branch to fix all bugs in TCL8.6.1 and to test enhancements too.
When a connect terminates to quick so the notifier is not ready yet, the connect is ignored and thus it waits forever for it.
Version | Status |
---|---|
8.5.15 | bug present |
8.5.16+ | fixed |
8.6.1 | bug present |
8.6.2+ | fixed in branch bug-13d3af3ad5 |
Test is timing dependent and may ignore issue on some machines.
Version | Status |
---|---|
8.5.15 | bug present |
8.5.16+ | fixed |
8.6.1 | bug present |
8.6.2+ | fixed |
The test is difficult, as an async connect must fail after a puts is issued on the channel.
Idea: write a dummy channel driver, which may be set to an error state by fconfigure -seterror and where the readbale/writable state may be set. So one could:
set h [open dummy] fconfigure $h -seterror EWOULDBLOCK fileevent $h writable {set x writable} fconfigure $h -blocking 0 puts $h abc fconfigure $h -setwritable 1 vwait x catch {close $h} e d
Version | Status |
---|---|
8.5.15 win | bug present |
8.5.16+ win | Fixed |
8.6.1 win | bug present |
8.6.2+ win | Fixed in bug-13d3af3ad5 |
If a socket connect fails, the error in the latest connect stage should be returned. This would prioritize "access denied" (e.g. socket in use) before "network unreachable" (no route).
Project stage for Win and Unix.
This is already implemented for Unix server sockets.
The Win TCL stubs table contains an entry for TclWinGetSockOpt() which returns the info from getsockopt().
In TCL8.5, the result of fconfigure -error was always the return value of the system call getsockopt(). In TCL8.6, a connect failure is cached in a variable and returned by fconfigure -error. Eventually, this should also be done by the routine called by the TclWinGetSockOpt stubs entry.
The purpose of this stub entry seams to be from the times of Windows 98 where a WinSock2.dll may not be present. There are no known usage of this. Thus it was decided to leave it as depreciated and to remove it for Tcl9.0.