1

我发现这里讨论的一个选项在主机连接到网络时效果很好。但是,socket.gethostbyname(hostname)如果主机未连接,则会挂起很长时间。

我看到一个建议socket.gethostbyname(hostname)在线程中运行,如果该线程在指定时间内没有返回结果,则假设它没有连接。我认为这是一个好主意,但是我对线程的熟练程度还不够(尽管我已经成功地使用了它们)知道如何做到这一点。

我发现了这个讨论如何在 Python 中找到线程的运行时间,这似乎暗示这不是微不足道的。有任何想法吗?谢谢。

编辑:

我必须承认我自己的无知。我没有意识到(尽管我应该知道)这socket.gethostbyname(hostname)是在进行 DNS 查找。所以,我把这个简单的东西放在一起来测试到端口 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() 

注意:这不会检查与网络的现有连接,如果未连接,则会挂起。

此脚本将首先检查与网络的连接,如果找到连接,则它将检查该网络上的特定主机:

#! /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()
4

2 回答 2

2

这里很有可能gethostbyname实际上不需要阻塞调用。

首先,您可能根本不想进行 DNS 查找,而且几乎您可以使用套接字执行的所有其他操作(例如,connect已经处理超时)。

其次,如果您确实需要 DNS 查找超时,您可能想要使用像pycares这样的异步 DNS 库。

但是,如果您需要 DNS 查找超时,并且您不能依赖外部代码,那么您是对的,您将不得不在另一个线程中运行 DNS 查找,并在主线程中等待它。

那么,你是怎么做到的呢?

好吧,您可以join使用超时的线程。或者,您可以等待后台线程可以发出信号的 或 ,或者使用Condition,等待后台线程可以写入的 a 。Eventselectpipe

最简单的事情可能join是超时......除了你最终让后台线程在超时后运行,如果你试图在它完成之前退出,Python 可能(并且将会,在大多数主要平台上使用 CPython 2.7 或 3.3)等待在退出之前结束。解决这个问题的方法是使用daemon线程,但是你不能合法地使用join它。您可以在超时后对其进行守护,但我认为这里Event更简单。

因此,例如:

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)

这是一个通用包装器:

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)

您可以像这样使用它:

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

将 anEvent用于比这更琐碎的事情变得棘手(而且您通常看不到它的棘手,并且编写的代码在 99% 的时间内都可以工作,并且无法调试其他 1% 的代码)。通常的问题是您可能会错过set后台线程。如果您不关心是否set在您检查之前或仅在您开始等待之后发生,您可以使用Event; 否则,您需要Condition.

于 2013-07-02T19:50:35.613 回答
0

如果你在 Windows 系统上,你可以使用这个

x = os.system("ping -n 1  google.com")
if x == 0: 
    print "HOST IS CONNECTED  :) "
于 2013-07-02T19:31:45.823 回答