Building a Port Scanner with Python

Table of contents

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 communicationthreading
: for concurrent scansipaddress
: 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)
Legal Warning
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.
Subscribe to my newsletter
Read articles from Michelle Mukai directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
