CCCPaste

df3574cb64db9

#!/usr/bin/env python3
"""
send_file.py - Split a file into 3KB chunks and send them to a remote host.

Usage:
    python send_file.py   
"""

import argparse
import socket
import struct
import sys
import time

CHUNK_SIZE = 3 * 1024  # 3 KB
MAX_RETRIES = 10
TIMEOUT_SECONDS = 5
RETRY_DELAY_SECONDS = 2


def send_chunk(host: str, port: int, chunk_index: int, total_chunks: int, data: bytes) -> None:
    """
    Send a single chunk to the remote host with retry logic.

    """
    payload = data
    size = len(payload)

    for attempt in range(1, MAX_RETRIES + 1):
        try:
            with socket.create_connection((host, port), timeout=TIMEOUT_SECONDS) as sock:
                sock.sendall(payload)
            print(f"  Chunk {chunk_index + 1}/{total_chunks} of {size} bytes sent successfully (attempt {attempt}).")
            time.sleep(1)
            return
        except (socket.timeout, TimeoutError):
            print(f"  Chunk {chunk_index + 1}/{total_chunks}: timeout on attempt {attempt}/{MAX_RETRIES}.")
        except OSError as exc:
            print(f"  Chunk {chunk_index + 1}/{total_chunks}: error on attempt {attempt}/{MAX_RETRIES}: {exc}")

        if attempt < MAX_RETRIES:
            print(f"  Retrying in {RETRY_DELAY_SECONDS}s...")
            time.sleep(RETRY_DELAY_SECONDS)

    raise RuntimeError(
        f"Failed to send chunk {chunk_index + 1}/{total_chunks} after {MAX_RETRIES} attempts."
    )


def split_and_send(filename: str, host: str, port: int) -> None:
    """Read the file, split it into chunks, and send each chunk."""
    try:
        with open(filename, "rb") as f:
            file_data = f.read()
    except FileNotFoundError:
        sys.exit(f"Error: file '{filename}' not found.")
    except OSError as exc:
        sys.exit(f"Error reading '{filename}': {exc}")

    chunks = [file_data[i : i + CHUNK_SIZE] for i in range(0, len(file_data), CHUNK_SIZE)]
    total_chunks = len(chunks)

    if total_chunks == 0:
        sys.exit("Error: file is empty.")

    print(f"File: '{filename}' ({len(file_data)} bytes) -> {total_chunks} chunk(s) of up to {CHUNK_SIZE} bytes.")
    print(f"Destination: {host}:{port}  (timeout={TIMEOUT_SECONDS}s, max_retries={MAX_RETRIES})\n")

    for index, chunk in enumerate(chunks):
        send_chunk(host, port, index, total_chunks, chunk)

    print("\nAll chunks sent successfully.")


def main() -> None:
    parser = argparse.ArgumentParser(description="Send a file in 3 KB chunks over TCP.")
    parser.add_argument("filename", help="Path to the file to send.")
    parser.add_argument("host", help="Remote host IP or hostname.")
    parser.add_argument("port", type=int, help="Remote port number.")
    args = parser.parse_args()

    split_and_send(args.filename, args.host, args.port)


if __name__ == "__main__":
    main()