I found an option discussed here which works great if the host is connected to the network. However, socket.gethostbyname(hostname)
hangs for a long time if the host is not connected.
I saw a suggestion to run socket.gethostbyname(hostname)
in a thread and if this thread did not return a result within a specified period, assume it is not connected. This I thought was a good idea, but I am not proficient enough yet with threads (although I have used them successfully) to know how to do this.
I found this discussion How to find running time of a thread in Python which seems to imply that this is not trivial. Any ideas? Thanks.
Edit:
I must admit my own ignorance. I didn't realize (though I should have) that socket.gethostbyname(hostname)
was doing a DNS lookup. So, I put together this simple to test for a socket connection to the host of interest on port 22:
#! /usr/bin/python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)
try:
s.connect(('192.168.2.5',22))
except Exception, e:
print 'connection failed'
s.close()
Note: this will not check for an existing connection to a network and will hang if not connected.
This script will check for a connection to a network first, if a connection is found then it will check for a specific host on that network:
#! /usr/bin/python
import socket
import fcntl
import struct
def check_connection():
ifaces = ['eth0','wlan0']
connected = []
i = 0
for ifname in ifaces:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
connected.append(ifname)
print "%s is connected" % ifname
except:
print "%s is not connected" % ifname
i += 1
return connected
connected_ifaces = check_connection()
if len(connected_ifaces) == 0:
print 'not connected to any network'
else:
print 'connected to a network using the following interface(s):'
for x in connected_ifaces:
print '\t%s' % x
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)
try:
s.connect(('192.168.2.5',22))
print 'connected to hostname'
except Exception, e:
print 'connection to hostname failed'
s.close()
There's a good chance that the blocking call to
gethostbyname
isn't actually necessary here.First, you may not want to do a DNS lookup at all, and almost everything else you can do with sockets—e.g.,
connect
—already handles timeouts.Second, if you really do need timeouts on DNS lookup, you probably want to use an async DNS library like pycares.
But if you need timeouts on DNS lookups, and you can't rely on external code, then you're right, you will have to run the DNS lookup in another thread, and wait on it in the main thread.
So, how do you do that?
Well, you can
join
a thread with a timeout. Or you can wait on aCondition
orEvent
that the background thread can signal, or, withselect
, on apipe
that the background thread can write to.The simplest thing is probably
join
with a timeout… except that you end up leaving the background thread running after a timeout, and if you try to quit before it finishes, Python may (and will, with CPython 2.7 or 3.3 on most major platforms) wait around for it to end before quitting. The way to fix that is to use adaemon
thread, but then you can't legallyjoin
it. You can daemonize it after the timeout, but I think here anEvent
is simpler.So, for example:
Here's a general-purpose wrapper:
Which you can use like this:
Using an
Event
for anything less trivial than this gets tricky (and often you can't see that it's tricky, and write code that works 99% of the time and is impossible to debug the other 1%). The usual problem is that you can miss theset
from the background thread. If you don't care whether theset
happens before you even checked, or only after you've started waiting, you can useEvent
; otherwise, you needCondition
.if you are on windows system you may use this