Search code examples
pythonpingnetwork-monitoring

Are there any alternatives to using system ping utility or sending ICMP packets for creating a network condition monitor?


I've been writing and maintaining a personal use utility that pings 8.8.8.8 every 1s and displays a pip indicating the network's condition for a while now, and due to the fact that my network appears to have inherent ~1.7% packet drop, I made it run two PING.EXE processes in parallel, with a timeout of 950ms so as to differentiate between randomly dropped packets and brief outages(these also happen)

Now, the problem is: I don't have enough control over the pings, and I've run into issues with the fact that PING.EXE's delay of 1s is not exact enough, and as the processes drift, I randomly get heavily degenerate cases like 60 pips correlating to 107.24s instead of the intended 60s.

I could probably work around this issue by launching a new ping process every second, but that's an extremely inelegant solution, and with non-negligible overhead(on Windows at least) besides, and I'd like to refrain from needing to manually send ICMP packets because then I'd need to run my utility with administrative privileges.

Apologies for the open-ended "is there anything" question, but without knowing anything, I don't know what to ask for.


EDIT: I've tried lowering the timeout from 999ms to 900ms and even 750ms, but PING.EXE either does not adhere to it, or it does so improperly, because this keeps happening, although the issue is much smaller - +15s under maximum network load. Probably acceptable enough, but...


Solution

  • If you need to replicate ping exactly in Python, you would need administrator privileges to send ICMP packets to a host.

    You could instead rely on TCP packets and tap the http port. That does not require admin privileges. Neither is it quite as accurate but it is an more accurate portrayal of web browsing ability.

    Simplifying this TCP ping code:

    import sys
    import socket
    import time
    import signal
    from timeit import default_timer as timer
    
    host = "google.com"
    port = 80
    
    # Default to 10000 connections max
    maxCount = 10000
    count = 0
    
    # Pass/Fail counters
    passed = 0
    failed = 0
    
    def getResults():
        """ Summarize Results """
    
        lRate = 0
        if failed != 0:
            lRate = failed / (count) * 100
            lRate = "%.2f" % lRate
    
        print("\nTCP Ping Results: Connections (Total/Pass/Fail): [{:}/{:}/{:}] (Failed: {:}%)".format((count), passed, failed, str(lRate)))
    
    def signal_handler(signal, frame):
        """ Catch Ctrl-C and Exit """
        getResults()
        sys.exit(0)
    
    # Register SIGINT Handler
    signal.signal(signal.SIGINT, signal_handler)
    
    # Loop while less than max count or until Ctrl-C caught
    while count < maxCount:
    
        # Increment Counter
        count += 1
    
        success = False
    
        # New Socket
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        # 1sec Timeout
        s.settimeout(1)
    
        # Start a timer
        s_start = timer()
    
        # Try to Connect
        try:
            s.connect((host, int(port)))
            s.shutdown(socket.SHUT_RD)
            success = True
    
        # Connection Timed Out
        except socket.timeout:
            print("Connection timed out!")
            failed += 1
        except OSError as e:
            print("OS Error:", e)
            failed += 1
    
        # Stop Timer
        s_stop = timer()
        s_runtime = "%.2f" % (1000 * (s_stop - s_start))
    
        if success:
            print("Connected to %s[%s]: tcp_seq=%s time=%s ms" % (host, port, (count-1), s_runtime))
            passed += 1
    
        # Sleep for 1sec
        if count < maxCount:
            time.sleep(1)
    
    # Output Results if maxCount reached
    getResults()
    

    Once you can create a 'ping' you could encapsulate the result of each time / ping result into an object stream to display as you wish.