Building a Port Scanner with Python

Michelle MukaiMichelle Mukai
4 min read

Introduction

Port scanning is a technique used to identify open ports and services available on a target machine. It's commonly used in network auditing and vulnerability assessments. In this article, you'll learn how to write a professional-grade port scanner in Python using only standard libraries.

This scanner will allow you to:

  • Scan a single host or multiple hosts

  • Specify custom port ranges

  • Display service names for common ports

  • Handle errors gracefully and efficiently

What Is Port Scanning?

Every service running on a computer listens on a specific port (e.g., HTTP on port 80, SSH on 22). A port scanner attempts to connect to these ports to determine which ones are open and possibly identify the associated service. This information is critical for:

  • Security auditing

  • Troubleshooting network issues

  • Mapping network services

Prerequisites

This script uses only built-in Python libraries, so there’s no need to install anything extra:

  • socket: for network communication

  • threading: for concurrent scans

  • ipaddress: for validating and handling IPs

This article walks you through creating a fast, multi-threaded port scanner using Python’s standard libraries. The scanner accepts a target IP address and scans a range of ports to identify open services.

Step 1: Import Required Libraries

import socket
import threading
import ipaddress
from queue import Queue

Explanation:

  • socket: Used to make TCP connections to test if ports are open.

  • threading: Enables concurrent port scanning to speed things up.

  • ipaddress: Validates IP addresses for proper format.

  • queue.Queue: Thread-safe queue to manage which ports to scan.

Step 2: Create Shared Queues and Storage

port_queue = Queue()
open_ports = []

Explanation:

  • port_queue will hold all ports that need scanning.

  • open_ports stores results — a list of tuples: (port number, service name).

Step 3: Define Function to Scan a Single Port

def scan_port(ip, port):
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(1.0)  # timeout in seconds
            result = s.connect_ex((ip, port))
            if result == 0:
                try:
                    service = socket.getservbyport(port)
                except:
                    service = "Unknown"
                print(f"[OPEN] Port {port:>5} ({service})")
                open_ports.append((port, service))
    except Exception:
        pass

Explanation:

  • Tries to connect to the port.

  • If connect_ex() returns 0, the port is open.

  • Uses getservbyport() to identify standard service (like HTTP or SSH).

  • Adds to open_ports list and prints the result.

Step 4: Define Worker Function for Threads

def worker(ip):
    while not port_queue.empty():
        port = port_queue.get()
        scan_port(ip, port)
        port_queue.task_done()

Explanation:

  • Each thread repeatedly pulls a port from the queue and scans it.

  • task_done() signals the queue that a port is completed.

Step 5: Define the Main Scanning Function

def port_scanner(target_ip, start_port=1, end_port=1024, num_threads=100):
    try:
        ip = str(ipaddress.ip_address(target_ip))  # Validate IP
    except ValueError:
        print("Invalid IP address.")
        return

    print(f"\nStarting scan on {ip} (Ports {start_port}-{end_port})...\n")

    # Fill the queue with port numbers
    for port in range(start_port, end_port + 1):
        port_queue.put(port)

    # Start threads
    threads = []
    for _ in range(num_threads):
        thread = threading.Thread(target=worker, args=(ip,))
        thread.daemon = True
        thread.start()
        threads.append(thread)

    # Wait for all threads to finish
    port_queue.join()
    print(f"\nScan complete. {len(open_ports)} open ports found.")

Explanation:

  • Validates IP address using ipaddress.

  • Loads port numbers into the queue.

  • Spawns worker threads (default: 100) to speed up scanning.

  • Waits until the queue is processed (join() blocks until empty).

  • Displays the number of open ports found.

Step 6: Add User Interaction

if __name__ == "__main__":
    target = input("Enter IP address to scan: ").strip()
    start = input("Start port [default 1]: ").strip()
    end = input("End port [default 1024]: ").strip()

    start_port = int(start) if start else 1
    end_port = int(end) if end else 1024

    port_scanner(target, start_port, end_port)

Explanation:

  • Prompts the user for IP and port range.

  • Converts empty input to default values.

  • Calls the scanner function with those parameters.

Complete Script

import socket
import threading
import ipaddress
from queue import Queue

port_queue = Queue()
open_ports = []

def scan_port(ip, port):
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(1.0)
            result = s.connect_ex((ip, port))
            if result == 0:
                try:
                    service = socket.getservbyport(port)
                except:
                    service = "Unknown"
                print(f"[OPEN] Port {port:>5} ({service})")
                open_ports.append((port, service))
    except Exception:
        pass

def worker(ip):
    while not port_queue.empty():
        port = port_queue.get()
        scan_port(ip, port)
        port_queue.task_done()

def port_scanner(target_ip, start_port=1, end_port=1024, num_threads=100):
    try:
        ip = str(ipaddress.ip_address(target_ip))
    except ValueError:
        print("Invalid IP address.")
        return

    print(f"\nStarting scan on {ip} (Ports {start_port}-{end_port})...\n")

    for port in range(start_port, end_port + 1):
        port_queue.put(port)

    threads = []
    for _ in range(num_threads):
        thread = threading.Thread(target=worker, args=(ip,))
        thread.daemon = True
        thread.start()
        threads.append(thread)

    port_queue.join()
    print(f"\nScan complete. {len(open_ports)} open ports found.")

if __name__ == "__main__":
    target = input("Enter IP address to scan: ").strip()
    start = input("Start port [default 1]: ").strip()
    end = input("End port [default 1024]: ").strip()

    start_port = int(start) if start else 1
    end_port = int(end) if end else 1024

    port_scanner(target, start_port, end_port)

Use responsibly. Port scanning without permission is illegal in many jurisdictions. Always obtain authorization before scanning any network or host.


Summary

You’ve now built a fast, threaded, professional port scanner in Python. It’s efficient, customizable, and serves as a strong foundation for more advanced network tools. This can be extended for use in security audits, IT diagnostics, or as a learning project in ethical hacking.

Let me know if you’d like to integrate this with a GUI, export results to files, or build a network-wide scanner.

0
Subscribe to my newsletter

Read articles from Michelle Mukai directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Michelle Mukai
Michelle Mukai