杰瑞科技汇

Python getaddrinfo如何正确使用?

Of course! The getaddrinfo function is a cornerstone of network programming in Python. It's a powerful and versatile tool that translates a human-friendly hostname (like google.com) and service name (like http) into a list of network addresses that your program can actually use to connect.

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

Here’s a comprehensive guide covering what it is, how to use it, and practical examples.

What is getaddrinfo?

getaddrinfo is a Python wrapper around the C library function of the same name. Its main job is to perform hostname resolution. It takes a host and a service and returns a list of tuples, where each tuple contains the information needed to create a socket connection.

Why is it so useful?

  • Abstraction: You don't need to worry about whether an address is an IPv4 address (like 8.8.8) or an IPv6 address (like 2001:4860:4860::8888). getaddrinfo handles both.
  • Service Resolution: You don't need to remember that port 80 is for http and port 443 is for https. You can just pass the service name ('http', 'https').
  • Protocol Agnostic: It can find addresses for different socket types, like SOCK_STREAM (TCP) and SOCK_DGRAM (UDP).

The Function Signature

import socket
socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

Parameters:

Python getaddrinfo如何正确使用?-图2
(图片来源网络,侵删)
  • host: The hostname (e.g., "www.google.com") or IP address (e.g., "127.0.0.1" or "::1"). 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. The most common are:
    • socket.AF_INET: For IPv4 addresses.
    • socket.AF_INET6: For IPv6 addresses.
    • socket.AF_UNSPEC: The default. It means "any family," so it will return both IPv4 and IPv6 addresses if available.
  • type: The socket type. The most common are:
    • socket.SOCK_STREAM: For TCP (reliable, connection-oriented).
    • socket.SOCK_DGRAM: For UDP (unreliable, connectionless).
    • 0: The default. It means "any type," so it will return results for both TCP and UDP.
  • proto: The protocol. Usually 0 (default), which means any protocol. Can be socket.IPPROTO_TCP or socket.IPPROTO_UDP.
  • flags: Optional flags to modify the behavior. The most common is socket.AI_ADDRCONFIG, which ensures you only get addresses for your machine's configured IP types (e.g., won't return IPv6 if you have no IPv6).

Return Value:

A list of 5-tuples. Each tuple contains the following information:

  1. family: The address family (e.g., socket.AF_INET).
  2. type: The socket type (e.g., socket.SOCK_STREAM).
  3. proto: The protocol (e.g., socket.IPPROTO_TCP).
  4. canonname: The canonical name of the host (often empty).
  5. sockaddr: A tuple containing the address and port. For AF_INET, this is (address, port). For AF_INET6, it's (address, port, flow_info, scope_id).

Basic Examples

Let's start with some simple, practical examples.

Example 1: Get IPv4 and TCP addresses for Google's homepage

This is the most common use case. We want to find the address to connect to google.com on the http port.

Python getaddrinfo如何正确使用?-图3
(图片来源网络,侵删)
import socket
# Get address info for google.com on port 80 (http)
results = socket.getaddrinfo("google.com", "http")
print(f"Found {len(results)} potential addresses.\n")
for result in results:
    family, socktype, proto, canonname, sockaddr = result
    # We are only interested in TCP stream sockets
    if socktype == socket.SOCK_STREAM:
        print(f"Family: {family}, Type: {socktype}, Protocol: {proto}")
        print(f"  Socket Address: {sockaddr}")
        print("-" * 20)

Typical Output:

Found 6 potential addresses.
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('142.250.199.100', 80)
--------------------
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('142.250.199.102', 80)
--------------------
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('142.250.199.101', 80)
--------------------
Family: AddressFamily.AF_INET6, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('2a00:1450:4001:81a::200e', 80, 0, 0)
--------------------
Family: AddressFamily.AF_INET6, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('2a00:1450:4001:81a::2006', 80, 0, 0)
--------------------
Family: AddressFamily.AF_INET6, Type: SocketKind.SOCK_STREAM, Protocol: 6
  Socket Address: ('2a00:1450:4001:81a::2004', 80, 0, 0)
--------------------

Notice how it returned both IPv4 and IPv6 addresses because we used the default AF_UNSPEC.

Example 2: Get only IPv4 addresses

What if you specifically want to connect using IPv4? You can specify the family.

import socket
# Get only IPv4 TCP addresses for google.com
results = socket.getaddrinfo("google.com", "http", family=socket.AF_INET, type=socket.SOCK_STREAM)
print(f"Found {len(results)} IPv4 TCP addresses.\n")
for result in results:
    family, socktype, proto, canonname, sockaddr = result
    print(f"Family: {family}, Type: {socktype}")
    print(f"  Socket Address: {sockaddr}")
    print("-" * 20)

Typical Output:

Found 3 IPv4 TCP addresses.
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM
  Socket Address: ('142.250.199.100', 80)
--------------------
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM
  Socket Address: ('142.250.199.102', 80)
--------------------
Family: AddressFamily.AF_INET, Type: SocketKind.SOCK_STREAM
  Socket Address: ('142.250.199.101', 80)
--------------------

How to Use the Result: A Practical Example

The real power of getaddrinfo is using it to create a socket connection. The sockaddr from the tuple is exactly what the socket.connect() method needs.

Here is a function that tries to connect to a host using the first available address from getaddrinfo. This is a robust way to handle connections.

import socket
def connect_to_host(hostname, port):
    """
    Tries to connect to a host using the first available address from getaddrinfo.
    """
    print(f"Attempting to connect to {hostname}:{port}...")
    try:
        # Get all possible addresses
        addr_info_list = socket.getaddrinfo(hostname, port, family=socket.AF_UNSPEC, type=socket.SOCK_STREAM)
        # Try each address until one works
        for addr_info in addr_info_list:
            family, socktype, proto, canonname, sockaddr = addr_info
            print(f"  Trying address: {sockaddr}")
            s = None
            try:
                s = socket.socket(family, socktype, proto)
                s.connect(sockaddr)
                print(f"  Successfully connected to {sockaddr}!")
                return s  # Return the connected socket object
            except socket.error as e:
                print(f"  Failed to connect to {sockaddr}: {e}")
                if s:
                    s.close()
        # If we get here, no address worked
        print("Could not connect to any address.")
        return None
    except socket.gaierror as e:
        print(f"getaddrinfo error: {e}")
        return None
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return None
# --- Usage ---
# Connect to a non-existent service to see it try multiple addresses
# sock = connect_to_host("google.com", "nonexistent-service")
# Connect to a real service
sock = connect_to_host("google.com", "http")
if sock:
    print("\nConnection established. Sending a simple HTTP request...")
    request = "GET / HTTP/1.1\r\nHost: google.com\r\nConnection: close\r\n\r\n"
    sock.send(request.encode('utf-8'))
    response = sock.recv(4096)
    print("\n--- First 500 bytes of response ---")
    print(response.decode('utf-8', errors='ignore')[:500])
    sock.close()

Handling Errors

getaddrinfo can raise exceptions, so it's important to handle them.

  • socket.gaierror (Get Address Info Error): This is the most common error. It occurs when the hostname cannot be resolved.

    • Causes: Hostname doesn't exist, DNS server is down, typo in hostname.
    • Example: socket.gaierror: [Errno 8] nodename nor servname provided, or not known
  • socket.error or OSError: This can occur during the socket creation or connection phase, even if getaddrinfo succeeded.

    Causes: Connection refused, host is unreachable, network is down.

Summary

Feature Description
Purpose Resolve a hostname/service into network addresses for socket connections.
Key Strength Abstraction. Handles IPv4, IPv6, TCP, UDP, and service names automatically.
Return Value A list of 5-tuples, where the last element (sockaddr) is used for socket.connect().
Common Use Case Use socket.getaddrinfo(host, port) and then iterate through the results to try connecting to each address until one succeeds.
Error Handling Always wrap calls in a try...except socket.gaierror block to catch resolution failures.
分享:
扫描分享到社交APP
上一篇
下一篇