Of course! The getaddrinfo function is a powerful and essential tool in Python for network programming. It's a cross-platform way to resolve a hostname (like google.com) or a service name (like http) into a list of network addresses.

Here’s a comprehensive guide covering what it is, why it's useful, how to use it, and practical examples.
What is getaddrinfo?
getaddrinfo is a standard C function, and Python provides access to it via the socket module. Its main job is to act as a universal address resolver.
Instead of using separate functions like gethostbyname for IPv4 or getnodebyname for other information, getaddrinfo provides a single, consistent interface that can handle:
- IPv4 and IPv6 addresses
- Hostnames (e.g.,
python.org) - Service names (e.g.,
http,ftp,ssh) and their corresponding port numbers (e.g.,80,21,22) - Different types of sockets (TCP, UDP, etc.)
It returns a list of tuples, where each tuple contains all the necessary information to create a socket connection.

Why Use getaddrinfo?
- Simplicity and Portability: It abstracts away the differences between operating systems and network configurations. You write one piece of code that works everywhere.
- Future-Proof: It seamlessly handles both IPv4 and IPv6. If you use older functions, you might have to write extra code to support IPv6.
- Flexibility: You can specify exactly what kind of socket you want (TCP, UDP), the protocol family, and whether you prefer IPv4 or IPv6.
- Robustness: It returns a list of possible addresses. If the first one fails (e.g., due to a network issue), you can try the next one in the list, making your application more resilient.
The Function Signature
import socket socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
Parameters:
host: The hostname (e.g.,"www.google.com") or IP address (e.g.,"8.8.8.8"or"2001:4860:4860::8888"). Can beNoneto use the local host.port: The port number (e.g.,80) or service name (e.g.,"http").family: The address family. You can use constants from thesocketmodule.socket.AF_UNSPEC: The default. Returns both IPv4 and IPv6 addresses.socket.AF_INET: IPv4 only.socket.AF_INET6: IPv6 only.
type: The socket type.socket.SOCK_STREAM: TCP (the default if not specified).socket.SOCK_DGRAM: UDP.
proto: The protocol. Usually0(the default), which lets the system choose the appropriate protocol for thetype(e.g.,IPPROTO_TCPforSOCK_STREAM).flags: Optional flags to modify the behavior. The most common issocket.AI_ADDRCONFIG, which returns only addresses that the system is configured to use.
Return Value:
A list of 5-tuples. Each tuple has the following structure:
(family, type, proto, canonname, sockaddr)
family: The address family (e.g.,socket.AF_INET).type: The socket type (e.g.,socket.SOCK_STREAM).proto: The protocol (e.g.,6for TCP).canonname: The canonical name of the host (often an empty string).sockaddr: A tuple containing the address and port. This is the most important part. For IPv4, it's(ip_address, port). For IPv6, it's(ip_address, port, flow_info, scope_id).
Practical Examples
Example 1: Basic Usage (Resolving a Hostname for TCP)
This is the most common use case. We want to find the IPv4 address for python.org on port 80 (HTTP).
import socket
# The hostname and service we want to resolve
hostname = "python.org"
service = "http" # Could also be port 80
print(f"Resolving addresses for '{hostname}' on service '{service}'...\n")
# Get the address information
# We ask for IPv4 (AF_INET) and TCP (SOCK_STREAM) sockets
try:
addr_info = socket.getaddrinfo(hostname, service, family=socket.AF_INET, type=socket.SOCK_STREAM)
except socket.gaierror as e:
print(f"Error resolving address: {e}")
exit()
# The result is a list of tuples. Let's inspect them.
print(f"Found {len(addr_info)} possible address(es):\n")
for family, type, proto, canonname, sockaddr in addr_info:
print(f" Family: {family} (AF_INET)")
print(f" Type: {type} (SOCK_STREAM)")
print(f" Proto: {proto}")
print(f" Address: {sockaddr[0]}")
print(f" Port: {sockaddr[1]}")
print("-" * 20)
Expected Output:

Resolving addresses for 'python.org' on service 'http'...
Found 1 possible address(es):
Family: 2 (AF_INET)
Type: 1 (SOCK_STREAM)
Proto: 6
Address: 151.101.65.224
Port: 80
--------------------
(Note: The IP address might vary slightly depending on your location and DNS routing.)
Example 2: Getting Both IPv4 and IPv6
By using AF_UNSPEC (the default), we can get all available address types.
import socket
hostname = "google.com"
port = 443 # https
print(f"Resolving all addresses (IPv4 & IPv6) for '{hostname}' on port {port}...\n")
try:
# We don't specify family or type to get all combinations
addr_info = socket.getaddrinfo(hostname, port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM)
except socket.gaierror as e:
print(f"Error resolving address: {e}")
exit()
print(f"Found {len(addr_info)} possible address(es):\n")
for family, type, proto, canonname, sockaddr in addr_info:
family_name = "AF_INET" if family == socket.AF_INET else "AF_INET6"
sockaddr_str = sockaddr[0] if family == socket.AF_INET else sockaddr[0] + '%' + str(sockaddr[3]) # Scope ID for link-local
print(f" Family: {family} ({family_name})")
print(f" Address: {sockaddr_str}")
print(f" Port: {sockaddr[1]}")
print("-" * 20)
Expected Output (will vary):
Resolving all addresses (IPv4 & IPv6) for 'google.com' on port 443...
Found 4 possible address(es):
Family: 2 (AF_INET)
Address: 142.250.199.100
Port: 443
--------------------
Family: 2 (AF_INET)
Address: 142.250.199.102
Port: 443
--------------------
Family: 23 (AF_INET6)
Address: 2607:f8b0:4006:805::2004
Port: 443
--------------------
Family: 23 (AF_INET6)
Address: 2607:f8b0:4006:805::2006
Port: 443
--------------------
Example 3: Practical Application - Creating a Resilient Socket Connection
This is the "real-world" way to use getaddrinfo. You iterate through the list of addresses until you successfully connect to one. This makes your application robust.
import socket
hostname = "python.org"
port = 80
# Get all possible TCP addresses (IPv4 and IPv6)
try:
addr_info = socket.getaddrinfo(hostname, port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM)
except socket.gaierror:
print(f"Could not resolve hostname: {hostname}")
exit()
print(f"Attempting to connect to {hostname} on port {port}...\n")
# Try each address until one succeeds
for family, type, proto, canonname, sockaddr in addr_info:
print(f"Trying address: {sockaddr[0]}...")
try:
# Create a socket
s = socket.socket(family, type, proto)
# Set a timeout so we don't hang forever
s.settimeout(5)
# Attempt to connect
s.connect(sockaddr)
print(f"Successfully connected to {sockaddr[0]}!")
s.close()
break # Exit the loop on success
except socket.error as e:
print(f"Failed to connect to {sockaddr[0]}: {e}")
continue # Try the next address
else:
# This 'else' block runs only if the loop completes without a 'break'
print("Could not connect to any address.")
Summary: Key Takeaways
| Feature | Description |
|---|---|
| What it is | A universal network address resolver in Python's socket module. |
| Why use it | Handles IPv4/IPv6, is portable, flexible, and makes your app resilient. |
| How to use | Call socket.getaddrinfo(host, port, ...) and iterate through the list of results. |
The sockaddr |
The 4th/5th element of the tuple is the crucial part. It contains the (address, port) info needed for socket.connect(). |
| Best Practice | Always loop through the list of addresses returned by getaddrinfo and attempt to connect to each one until you succeed. |
