I need to check if a host is up or down quickly. For this, I added a socket timeout before making a connection, but the socket timeout doesn't seem to work. Why?
Here's my code:
import socket
def test_hostname(hostname, port):
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(1.0) # timeout
try:
s.connect((hostname, port))
except (socket.timeout, socket.gaierror):
return False
else:
return True
finally:
s.close()
result = test_hostname("001.com",80) # 001.com is down
print(result)
I takes around 10-20 seconds instead of the specified timeout (1 second).
CodePudding user response:
This happens because the timeout for a Python socket does not limit the amount of time spent on DNS lookup. Since you are using a domain name that needs to be resolved to an IP, your s.connect((hostname, port))
will first need to make a DNS query for hostname
, and only then it will be able to create a connection using one of the IPs returned by the DNS server (if any).
The domain 001.com
does not seem to resolve to any IP address, so it can take several seconds for your system to return failure. When this happens, Python will bail out and the connection will fail, but the timeout will already be long expired.
If you know the IP address of the host you want to reach, use that instead of the hostname. If not, you will have to use a DNS resolution library to also time out DNS resolution, like for example dnspython
:
import socket
import dns.resolver
def test_hostname(hostname, port):
resolver = dns.resolver.Resolver()
resolver.timeout = resolver.lifetime = 1
try:
answer = resolver.resolve(hostname, 'A')
except dns.exception.Timeout:
return False
if not answer.rrset:
return False
addr = next(iter(answer.rrset)).to_text()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
try:
s.connect((addr, port))
except (socket.timeout, socket.gaierror):
return False
else:
return True
finally:
s.close()