Testing socketports


(See modification at the bottom of the page)

socktest.tcl

 ################################################################################
 # Module   : socktest.tcl
 # Last Chg.: 30.10.2005
 # Purpose  : test availability of a sockets-port without waiting on a
 #            nonconfigurable, os dependant timeout, using an async socket; test
 #            if a local socket server can be started at a given port
 # Author   : M.Hoffmann, partially based on https://wiki.tcl-lang.org/1114
 # ToDo     : more tests
 # History
 # 29.10.05 : generalized as a simple package, namespace, test, optimized,
 #            enhanced
 ################################################################################

 package provide socktest 0.1

 namespace eval socktest {

    namespace export socktest sockmesg localsockfree

    variable resulttext
    array set resulttext {
       -2 SocketError
       -1 NameError
        0 Timeout
        1 OK
        9 Undefined
    }

    # test if port 'sock' at adress 'host' is responding
    proc socktest {host sock {timeout 1000}} {
         if {[catch {socket -async $host $sock} s]} {
            return -1
         }
         variable done$sock 9; # allow parallel instances
         # if socket becomes writable, test further
         fileevent $s writable [list namespace eval socktest "sockvrfy $s done$sock"]
         # prepare for cancellation after user supplied timeout
         set aid [after $timeout namespace eval socktest "set done$sock 0"]
         # waiting for timeout or other result
         vwait [namespace current]::done$sock
         catch {close $s}
         after cancel $aid; # catch not neccessary
         set ret [set done$sock]
         unset done$sock; # save mem
         return $ret
    }

    proc sockvrfy {sock flag} {
         upvar $flag done
         if {[string length [fconfigure $sock -error]] == 0} {
            set done  1
         } else {
            set done -2
         }
    }

    proc sockmesg {rc} {
         variable resulttext
         catch {set resulttext($rc)} ret
         return $ret
    }

    # test if port 'sock' at localhost is available or already in use
    proc localsockfree {sock} {
         if {[catch {socket -server {} $sock} rc]} {
            return 0
         } else {
            # server could be started, so the port is not in use locally
            catch {close $rc}
            return 1
         }
    }

 }

socktest_test.bat

 ::if 0 {
 @tclsh %~n0.bat %* & @goto :EOF
 }
 # test the socktest-package, 30.10.2005
 lappend auto_path ./
 package require socktest 0.1
 namespace import socktest::*
 # puts [info commands socktest::*]
 # test parallel behaviour
 after 10000 [list set done 1]
 foreach {host sock} {wronghost wrongport
                      localhost wrongport
                      localhost 80
                      localhost ftp
                      wrong wrong} {
         after 1000 puts [sockmesg [socktest $host $sock 3000]]
 }
 vwait done
 puts [localsockfree 80]
 puts [localsockfree 23]
 puts [localsockfree ftp]

pkgIndex.tcl

 package ifneeded socktest 0.1 [list source [file join $dir socktest.tcl]]

################################################################################
# Module   : socktest.tcl
# Last Chg.: 02.03.2015
# Purpose  : test availability of a sockets-port without waiting on a non
#            configurable, os dependant timeout, using an async socket; test if
#            a local socket server can be started at a given port
# Author   : M.Hoffmann, partially based on https://wiki.tcl-lang.org/1114
# ToDo     : more tests
# History
# 30.10.05 : generalized as a simple package, namespace, test, optimized,
#            enhanced
# 02.03.15 : Timeout 0 means not using -async and vwait to avoid unintentionally
#             creating nested eventloops. Relies on OS-timeout then, sorry.
#            Timout should only be used in simple programs where no complicated
#            events are used....
################################################################################

package provide socktest 0.2

namespace eval socktest {

   namespace export socktest sockmesg localsockfree

   variable resulttext
   array set resulttext {
      -2 SocketError
      -1 NameError
       0 Timeout
       1 OK
       9 Undefined
   }

   # test if port 'sock' at adress 'host' is responding within timeout
   # note: socket -async requires a running eventloop
   proc socktest {host sock {timeout 1000}} {
        if {$timeout == 0} {
           # return codes compatible
           if {[catch {socket $host $sock} s]} {
              return -2
           } else {
              catch {close $s}
              return 1
           }
        }
        if {[catch {socket -async $host $sock} s]} {
           return -1
        }
        variable done$sock 9; # allow parallel instances
        # if socket becomes writable, test further
        fileevent $s writable [list namespace eval socktest "sockvrfy $s done$sock"]
        # prepare for cancellation after user supplied timeout
        set aid [after $timeout namespace eval socktest "set done$sock 0"]
        # waiting for timeout or other result
        vwait [namespace current]::done$sock
        catch {close $s}
        after cancel $aid; # catch not neccessary
        set ret [set done$sock]
        unset done$sock; # save mem
        return $ret
   }

   proc sockvrfy {sock flag} {
        upvar $flag done
        if {[string length [fconfigure $sock -error]] == 0} {
           set done  1
        } else {
           set done -2
        }
   }

   proc sockmesg {rc} {
        variable resulttext
        catch {set resulttext($rc)} ret
        return $ret
   }

   # test if port 'sock' at localhost is available or already in use
   proc localsockfree {sock} {
        if {[catch {socket -server {} $sock} rc]} {
           return 0
        } else {
           # server could be started, so the port is not in use locally
           catch {close $rc}
           return 1
        }
   }

}