杰瑞科技汇

Python如何实现tracert功能?

Of course! Creating a Python traceroute is a fantastic project that teaches you about network protocols, sockets, and ICMP. I'll guide you through it, starting with a simple version and then showing a more robust, cross-platform one using a popular library.

Python如何实现tracert功能?-图1
(图片来源网络,侵删)

Understanding How Traceroute Works

At its core, traceroute works by exploiting a feature of the IP protocol: Time-To-Live (TTL).

  1. TTL: Every IP packet has a TTL field. It's a counter that starts at a certain value (e.g., 64) and is decremented by one by each router it passes through. When the TTL reaches zero, the router discards the packet and sends an ICMP "Time Exceeded" message back to the source.
  2. The Process:
    • To find the first router, you send a packet with a TTL of 1. The first router will decrement it to 0, discard it, and send back an "Time Exceeded" message. You now know the first hop.
    • To find the second router, you send a packet with a TTL of 2. The first router will decrement it to 1 and forward it. The second router will decrement it to 0, discard it, and send back the "Time Exceeded" message. You now know the second hop.
    • This process repeats, incrementing the TTL by 1 each time, until you either reach the destination or the destination explicitly responds (which happens with a TTL higher than what's needed to reach it).

Method 1: The "From Scratch" Python Traceroute (Linux/macOS)

This version is great for understanding the fundamentals. It uses raw sockets to build and send IP/UDP packets and listen for ICMP responses.

⚠️ Important: This code will only work on Linux or macOS because it requires creating a raw socket, which is restricted on Windows. You'll likely need to run it with sudo permissions.

import socket
import sys
import os
import time
import select
from typing import List, Tuple
# The destination port. We use a port that is likely not open to force
# an ICMP "Port Unreachable" message from the final destination.
DEST_PORT = 33434
def traceroute(hostname: str, max_hops: int = 30, timeout: float = 2.0) -> None:
    """
    Performs a traceroute to a given hostname.
    """
    try:
        # Resolve the hostname to an IP address
        dest_addr = socket.gethostbyname(hostname)
    except socket.gaierror:
        print(f"Error: Unable to resolve hostname '{hostname}'")
        return
    print(f"Tracing route to {hostname} [{dest_addr}] with max {max_hops} hops:\n")
    # Create a raw socket for sending and receiving
    # We use IPPROTO_ICMP to receive ICMP messages
    # We use IPPROTO_UDP to send UDP packets
    try:
        recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
        send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    except PermissionError:
        print("Error: This script requires root/administrator privileges to create raw sockets.")
        return
    except Exception as e:
        print(f"Error creating sockets: {e}")
        return
    recv_socket.bind(("", 0))  # Bind to any available port
    recv_socket.settimeout(timeout)
    for ttl in range(1, max_hops + 1):
        send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
        # Create a new socket pair for each TTL to avoid issues with port reuse
        # This is a simplified approach; a more robust solution would use a different source port
        # or handle port reuse more carefully.
        # For this example, we'll just send and receive.
        start_time = time.time()
        try:
            # Send a UDP packet to the destination
            send_socket.sendto(b"", (dest_addr, DEST_PORT))
        except Exception as e:
            print(f"Error sending packet for TTL {ttl}: {e}")
            break
        current_hop = None
        rtt = None
        done = False
        # Wait for a response with a timeout
        while not done:
            ready = select.select([recv_socket], [], [], timeout)
            if ready[0]:
                try:
                    packet, addr = recv_socket.recvfrom(512)
                    current_hop = addr[0]
                    end_time = time.time()
                    rtt = (end_time - start_time) * 1000  # in milliseconds
                    done = True
                except socket.timeout:
                    print(f" {ttl}.  * * * Request timed out.")
                    break
                except Exception as e:
                    print(f" {ttl}.  * * * Error receiving: {e}")
                    break
            else:
                print(f" {ttl}.  * * * Request timed out.")
                break
        if current_hop:
            print(f" {ttl}.  {current_hop}  {rtt:.1f} ms")
        # If we reached the destination, we can stop
        if current_hop == dest_addr:
            print("\nTrace complete.")
            break
    recv_socket.close()
    send_socket.close()
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 traceroute.py <hostname>")
        sys.exit(1)
    target = sys.argv[1]
    traceroute(target)

How to Run It:

  1. Save the code as traceroute.py.
  2. Open your terminal.
  3. Run it with sudo: sudo python3 traceroute.py google.com

Method 2: The Cross-Platform & Robust Way (Using a Library)

Writing a robust traceroute is complex. Handling different operating systems, packet types (UDP, TCP, ICMP), and edge cases is a lot of work. The best approach for real-world applications is to use a well-tested library.

Python如何实现tracert功能?-图2
(图片来源网络,侵删)

The pythontraceroute library is a great choice. It's cross-platform and handles the complexities for you.

Step 1: Install the Library

pip install pythontraceroute

Step 2: Use the Library

The library's API is very simple and clean.

from pythontraceroute import Traceroute
import time
def run_traceroute(hostname: str):
    """
    Performs a traceroute using the pythontraceroute library.
    """
    print(f"--- Tracing route to {hostname} ---")
    start_time = time.time()
    # The Traceroute object handles all the complexity
    tracer = Traceroute(hostname)
    # The run() method executes the traceroute and returns a list of hops
    hops = tracer.run()
    end_time = time.time()
    total_time = end_time - start_time
    print(f"\nTraceroute completed in {total_time:.2f} seconds.\n")
    for i, hop in enumerate(hops):
        # hop is a dictionary with 'ip', 'hostname', 'rtt1', 'rtt2', 'rtt3'
        ip = hop.get('ip', '* * *')
        hostname = hop.get('hostname', '* * *')
        # Print the hop number and IP/hostname
        print(f"{i + 1}. {ip} ({hostname})")
        # Print the round-trip times
        for j in range(1, 4):
            rtt = hop.get(f'rtt{j}')
            if rtt:
                print(f"   {rtt:.3f} ms")
            else:
                print(f"   * * *")
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 traceroute_lib.py <hostname>")
        sys.exit(1)
    target = sys.argv[1]
    run_traceroute(target)

How to Run It:

  1. Make sure you have the library installed (pip install pythontraceroute).
  2. Save the code as traceroute_lib.py.
  3. Run it from your terminal: python3 traceroute_lib.py google.com

Comparison of Methods

Feature Method 1 (From Scratch) Method 2 (Library)
Dependencies None (built-in socket) pip install pythontraceroute
Platform Linux/macOS only Cross-platform (Windows, Linux, macOS)
Permissions Requires sudo Does not require sudo
Robustness Low. Handles basic cases. May fail on complex networks or OSes. High. Handles packet loss, multiple probes, OS differences.
Code Complexity High. You manage sockets, TTL, ICMP, and timeouts. Low. A few lines of code to get a full-featured traceroute.
Learning Value Excellent. Teaches the core networking concepts. Low. Hides the complexity, good for quick results.

Recommendation

  • For learning and understanding: Use Method 1. It's a fantastic exercise that demystifies what traceroute and tracert are doing under the hood.
  • For a real application or script: Use Method 2. It's reliable, portable, and saves you from reinventing the wheel and dealing with platform-specific bugs.
Python如何实现tracert功能?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇