Python TFTP Server Progress Bar: Track File Downloads
Python TFTP Server Progress Bar: Track File Downloads

Python TFTP Server Progress Bar: Track File Downloads

Build a Python TFTP server with a progress bar using tftpy. Track file downloads, improve UX, and handle concurrent requests. 5 min


The Trivial File Transfer Protocol (TFTP) is a simple protocol used for transferring files between devices, often in network booting and embedded systems. Unlike FTP, it doesn’t support authentication, making it lightweight but lacking in advanced features.

When transferring large files over TFTP, having a progress bar can significantly improve the user experience. Without feedback, users may assume a transfer is stuck. In this guide, we’ll build a Python TFTP server with a progress bar to track file downloads effectively.

How TFTP Works and Why Use Python?

TFTP operates on a client-server model using UDP. It supports two primary operations:

  • Read request (RRQ): Client requests a file from the server.
  • Write request (WRQ): Client sends a file to the server.

Since TFTP lacks built-in visual indicators for progress, tracking file transfers can be tricky. Python provides the tftpy library, which allows custom hooks to monitor transfers. This makes it ideal for implementing a progress bar.

Setting Up a Basic TFTP Server in Python

First, install the tftpy library if you haven’t already:

pip install tftpy

Now, let’s create a basic TFTP server:


import tftpy

server = tftpy.TftpServer('tftp_root')
server.listen('0.0.0.0', 69)

This code initializes a TFTP server listening on port 69, serving files from the `tftp_root` directory. Ensure this folder exists and contains files for testing.

Adding a Progress Bar Using the Download Hook

The download_hook function in `tftpy` lets us track file downloads by providing:

  • context: The connection context.
  • file_path: The name of the file being transferred.
  • completed: Bytes transferred so far.
  • total: Total bytes of the file.

Here’s a simple way to print progress:


def download_hook(context, file_path, completed, total):
    percentage = (completed / total) * 100 if total else 0
    print(f"Downloading {file_path}: {percentage:.2f}% complete")

Now integrate it into the server:


server.download_hook = download_hook
server.listen('0.0.0.0', 69)

In this form, the function prints percentage updates every time new data arrives.

Enhancing the Progress Bar for Better Readability

Printing progress line by line can flood the terminal. Instead, we can overwrite the same line using `\r`:


import sys

def download_hook(context, file_path, completed, total):
    progress = int((completed / total) * 50) if total else 0
    bar = "[" + "=" * progress + " " * (50 - progress) + "]"
    sys.stdout.write(f"\rDownloading {file_path} {bar} {completed}/{total} bytes")
    sys.stdout.flush()

This provides a visual progress bar instead of just percentage numbers.

Making It Thread-Safe for Concurrent Requests

A live TFTP server may handle multiple downloads at once. To prevent interference in a multi-threaded environment, use threading locks:


import threading

lock = threading.Lock()

def download_hook(context, file_path, completed, total):
    with lock:
        progress = int((completed / total) * 50) if total else 0
        bar = "[" + "=" * progress + " " * (50 - progress) + "]"
        sys.stdout.write(f"\rDownloading {file_path} {bar} {completed}/{total} bytes")
        sys.stdout.flush()

This ensures that multiple threads don’t write to the terminal simultaneously, keeping the output clean.

Fixing Common TFTP Issues

If you encounter file not found errors, check:

  • The file exists in `tftp_root`.
  • Permission settings allow it to be read.

For client connection issues, ensure:

  • The TFTP server is running on port 69.
  • Firewall rules allow UDP traffic.

If the progress bar isn’t updating, verify `completed` and `total` values in the download_hook function.

Using Alternative Libraries for a Better Progress Bar

Libraries like tqdm and rich provide better visuals:

tqdm Example:


from tqdm import tqdm

pbar = None

def download_hook(context, file_path, completed, total):
    global pbar
    if not pbar:
        pbar = tqdm(total=total, desc=f"Downloading {file_path}", unit="B", unit_scale=True)
        
    pbar.n = completed
    pbar.refresh()

rich Example:


from rich.progress import Progress

progress = Progress()
task = None

def download_hook(context, file_path, completed, total):
    global task
    if task is None:
        task = progress.add_task(f"Downloading {file_path}", total=total)
        
    progress.update(task, completed=completed)

These libraries provide dynamic, colorful progress bars.

Complete Thread-Safe TFTP Server with a Progress Bar

Bringing all the improvements together, this is the refined version:


import tftpy
import sys
import threading

lock = threading.Lock()

def download_hook(context, file_path, completed, total):
    with lock:
        progress = int((completed / total) * 50) if total else 0
        bar = "[" + "=" * progress + " " * (50 - progress) + "]"
        sys.stdout.write(f"\rDownloading {file_path} {bar} {completed}/{total} bytes")
        sys.stdout.flush()

server = tftpy.TftpServer('tftp_root')
server.download_hook = download_hook
server.listen('0.0.0.0', 69)

Frequently Asked Questions

  • What is the default port for TFTP? TFTP operates on UDP port 69.
  • How do I test my TFTP server? Use a client like tftp-hpa (`tftp localhost -c get filename`).
  • Is TFTP secure? No, it lacks authentication and encryption. Use it only in controlled environments.
  • Can I use this with other languages? Yes, similar techniques apply to C, Java, and other languages.

Adding a simple progress bar greatly improves the usability of a TFTP server. Try enhancing it further by integrating async I/O for non-blocking operations or logging download speed.

Let us know in the comments if you have questions or suggestions!


Like it? Share with your friends!

Shivateja Keerthi
Hey there! I'm Shivateja Keerthi, a full-stack developer who loves diving deep into code, fixing tricky bugs, and figuring out why things break. I mainly work with JavaScript and Python, and I enjoy sharing everything I learn - especially about debugging, troubleshooting errors, and making development smoother. If you've ever struggled with weird bugs or just want to get better at coding, you're in the right place. Through my blog, I share tips, solutions, and insights to help you code smarter and debug faster. Let’s make coding less frustrating and more fun! My LinkedIn Follow Me on X

0 Comments

Your email address will not be published. Required fields are marked *