杰瑞科技汇

Python getaddrinfo如何使用?

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.

Python getaddrinfo如何使用?-图1
(图片来源网络,侵删)

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.

Python getaddrinfo如何使用?-图2
(图片来源网络,侵删)

Why Use getaddrinfo?

  1. Simplicity and Portability: It abstracts away the differences between operating systems and network configurations. You write one piece of code that works everywhere.
  2. Future-Proof: It seamlessly handles both IPv4 and IPv6. If you use older functions, you might have to write extra code to support IPv6.
  3. Flexibility: You can specify exactly what kind of socket you want (TCP, UDP), the protocol family, and whether you prefer IPv4 or IPv6.
  4. 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 be None to 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 the socket module.
    • 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. Usually 0 (the default), which lets the system choose the appropriate protocol for the type (e.g., IPPROTO_TCP for SOCK_STREAM).
  • flags: Optional flags to modify the behavior. The most common is socket.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., 6 for 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:

Python getaddrinfo如何使用?-图3
(图片来源网络,侵删)
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.
分享:
扫描分享到社交APP
上一篇
下一篇