1

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()
Community
  • 1
  • 1
nomadicME
  • 1,389
  • 5
  • 15
  • 35
  • http://stackoverflow.com/questions/3764291/checking-network-connection – NPE Jul 02 '13 at 19:33
  • @NPE thanks for the idea. I like the timeout option in urllib2.urlopen, but it seems to me you need to have a webserver running on that host for this to work, which I don't. – nomadicME Jul 02 '13 at 19:41
  • In cases where the network is reachable but the DNS server isn't, do you want to pass or fail? If the answer is "pass" or "don't care", you can put a timeout on a `connect` to an IP address much more easily than on a DNS lookup. If the answer is "fail" (and you don't care about distinguishing the cases), then you do have to do something complicated as you suggest. – abarnert Jul 02 '13 at 19:41
  • @nomadicME: That answer uses "one of the IP-addresses for google.com", so I think it's a good bet that a webserver will be running on that host. However, it's still a whole lot simpler to just `connect` than to try to do a whole HTTP request and response… – abarnert Jul 02 '13 at 19:42
  • The (hopefully) nice thing about your edited version is that it doesn't distinguish between no connectivity, server downtime, network splits, etc. For some apps, that's bad, but 99% of the time, it's good. After all, who cares if the network is up, if you can't reach the server you need? – abarnert Jul 02 '13 at 20:52
  • Thanks @abarnert, I was just starting to notice that a hang would occur if not connected to any network. I've updated my edit to include a more complete solution. – nomadicME Jul 02 '13 at 21:19
  • @nomadicME: Do you really want to do that? For one thing, that pretty much restricts your code to running on one computer (or, at least, to linux computers that have similar network setups—a dual-NIC linux box may connect to the internet via eth1; a FreeBSD or Mac via en0; a computer connected through a VPN through something else; etc.). – abarnert Jul 02 '13 at 21:34
  • @abarnert, for me this works just fine and others may find it helpful as well. I don't really have a need for a more general solution since every computer I own runs linux and has the same two network interfaces. The user can modify the list of interfaces if necessary. If the user requires a more general solution they can use the threaded method you describe in your answer. – nomadicME Jul 02 '13 at 21:42
  • @nomadicME: It's not a matter of more general; it's a matter of solving a completely different problem. Your code detects whether the interfaces are up or down, and then checks whether a certain address is reachable. The `gethostbyname` checks whether DNS on the primary interface(s) is properly configured and reachable. Almost every combination of those problems is possible. – abarnert Jul 02 '13 at 21:51
  • @abarnert by virtue of me asking the question, the problem to be answered was defined by me. I think you may have misunderstood the problem. – nomadicME Jul 02 '13 at 23:25

2 Answers2

2

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 a Condition or Event that the background thread can signal, or, with select, on a pipe 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 a daemon thread, but then you can't legally join it. You can daemonize it after the timeout, but I think here an Event is simpler.

So, for example:

event = threading.Event()

def blocking_dns():
    socket.gethostbyname(host)
    event.set()

thread = threading.Thread(target=blocking_dns)
thread.daemon = True
thread.start()
success = event.wait(timeout)

Here's a general-purpose wrapper:

def run_with_timeout(timeout, func, *args, **kwargs):
    event = threading.Event()
    def wrapper():
        func(*args, **kwargs)
        event.set()
    thread = threading.Thread(target=wrapper, args=args, kwargs=kwargs)
    thread.daemon = True
    thread.start()
    return event.wait(timeout)

Which you can use like this:

dns_works = run_with_timeout(5.0, socket.gethostbyname, 'www.google.com')

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 the set from the background thread. If you don't care whether the set happens before you even checked, or only after you've started waiting, you can use Event; otherwise, you need Condition.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • thank you for your very thorough answer. As I stated in my edit of the original question, a simple socket connection with timeout did the trick. However, I'm really glad you decided to answer the thread part of the question because I really wanted to know how to do that as well. – nomadicME Jul 02 '13 at 20:42
0

if you are on windows system you may use this

x = os.system("ping -n 1  google.com")
if x == 0: 
    print "HOST IS CONNECTED  :) "
blackwind
  • 182
  • 1
  • 10
  • Why do you think running a program that calls `gethostbyname` will be better than just calling it yourself? Why use `system` when the docs for it say you should almost never use it? Why do something that's Windows-specific when it's actually harder than doing it cross-platform? – abarnert Jul 02 '13 at 19:39
  • yes os.system shouldn't be used frequently , but in this case you may. First , PING is cross platform command and but *NIX and Windows can handle os.system without any issues. – blackwind Jul 02 '13 at 19:55
  • Windows `ping` doesn't have the exact same syntax as POSIX ping… but, more importantly, your answer specifically says "if you are on windows system", which strongly implies that it's a Windows-specific answer. – abarnert Jul 02 '13 at 19:58