A standard-library-only TCP file-transfer server framework built around a custom 3-byte binary handshake protocol.
NetForge is a small but complete client/server file-transfer stack. A
single-instance server accepts TCP connections, validates a role/application
handshake using integer control codes, logs everything to per-module log
files, and streams files with a length-prefixed wire format. A reference client
(General_User) performs the same handshake and then either uploads
(sender) or downloads (receiver) a file.
The whole conversation is just a few fixed-size frames:
Client Server
| |
| Frame 1: connect|client|request (3B) |
|---------------------------------------->|
| User ID: "alice\0" (utf-8 + NUL) |
|---------------------------------------->|
| Frame 2: accepted|server|client (3B) |
|<----------------------------------------|
| Frame 3: request|client|app (3B) | app = sender(0x3B)
|---------------------------------------->| or receiver(0x2B)
| Frame 4: accepted|server|app (3B) |
|<----------------------------------------|
| File frame: !Q len|name|!Q size|bytes |
|<--------------------------------------->|
| Transfer status: 0x09 ok / 0x10 fail |
|<----------------------------------------|
How the real components talk: the client drives the 3-byte handshake and then a
length-prefixed file frame; the server validates, then either stores an upload or
streams a download from FileServer/storage/. The dashboard launches/drives them
and the report generator turns connection records into CSV/TXT/JSON.
flowchart LR
DASH["dashboard.py<br/>CLI menu and --demo showcase"]
subgraph client["Client - General_User (Client.py)"]
C1["ConnToServer()<br/>3-byte handshake + user ID"]
C2["send_file() / request_file()<br/>uses _recv_exact()"]
end
subgraph server["Server - Server_Dashboard (ServerDashboard.py)"]
S1["StartServer()<br/>threaded accept loop"]
S2["verify_connection()<br/>UserHandler validates frames"]
S3["handle_file_transfer()<br/>recv_file / send_file / recv_exact"]
end
STORE[("FileServer/storage/")]
REP["Report_Generator<br/>CSV / TXT / JSON to reports/"]
LOGS["FileServer/*.log"]
DASH -.launches.-> S1
DASH -.demo drives.-> C1
DASH --> REP
C1 <-->|"handshake: 3B frames + accepted echoes"| S2
C1 --> C2
C2 <-->|"framed file: len, name, size, raw bytes + 1B status"| S3
S1 --> S2 --> S3
S3 <--> STORE
S2 --> LOGS
- 3-byte handshake state machine with server-side validation of command,
user type, user status, and application (
ServerHandler.UserHandler). - Two transfer roles:
senderuploads a file to the server;receiverdownloads a file by name from server storage. - Length-prefixed file framing (
!Qname length, name,!Qsize, raw bytes) streamed in 64 KiB chunks, shared by both client and server. - Concurrent accept loop: each client is handled in its own daemon thread; the listener wakes periodically to honour a clean shutdown flag.
- Path-traversal-safe storage (
os.path.basenameon all incoming names) and bounded reads (socket timeouts, capped user-ID length). - Per-module logging to
FileServer/*.logwith handler de-duplication. - CSV / TXT / JSON reports of connection data (
ReportGenerator). - Branded CLI dashboard (
dashboard.py) that drives every feature for real.
pip install -r requirements.txt # rich/psutil are optional; core needs nothing
python3 dashboard.py # interactive menu (branded, EOF/pipe-safe)
python3 dashboard.py --demo # scripted end-to-end showcase, exits 0
python3 dashboard.py --demo --json # machine-readable summary of the demoRepresentative python3 dashboard.py --demo output (trimmed):
╔═══════════════════════════════════════════════════════════════════════════╗
║ NetForge ║
║ TCP file-transfer server framework with a 3-byte handshake protocol ║
╚═══════════════════════════════════════════════════════════════════════════╝
Running NetForge --demo showcase
1) End-to-end loopback self-test (real handshake + file transfer)
[*] Server listening on 127.0.0.1:58311 (daemon thread)
[+] sender handshake -> uploaded 15360 bytes
[+] receiver handshake -> downloaded 15360 bytes
[+] handshake OK, upload OK, download OK, byte-for-byte round-trip OK
2) Report generation (real Report_Generator: CSV + JSON)
[+] CSV report: .../reports/connected_users_20260705_210224.csv
[+] JSON report: .../reports/connected_users_20260705_210224.json
[+] 3 connected-user records written
3) Protocol reference (real Server.user_status_code table)
Frame 1 (client -> server) 3 bytes connect(0xAA) | client(0x2A) | request(0x3C)
...
NetForge demo complete — all real features exercised successfully.
The --demo run binds an ephemeral loopback port, serves from a daemon
thread, performs a real sender upload and receiver download, verifies the bytes
match, and cleans up its temp files — no network or hardware required.
- Run end-to-end loopback self-test (handshake + file transfer)
- Print commands to run the real server / client
- Show protocol + status-code reference table
- Generate CSV/JSON report of a sample connected-users list
- Show host / runtime system stats
- Quit
python3 ServerDashboard.py --verbose # auto IP, first free port 8000-8500
python3 ServerDashboard.py --host 127.0.0.1 --port 8000 --verboseUploaded files are stored under FileServer/storage/; that same directory is
where the server looks for files a receiver asks to download.
# Upload a file (sender role)
python3 Client.py --host 127.0.0.1 --port 8000 --app sender --file ./myfile.txt
# Download a file (receiver role)
python3 Client.py --host 127.0.0.1 --port 8000 --app receiver --file myfile.txt --dest-dir ./gotfrom ServerDashboard import Server_Dashboard
from Client import General_User
server = Server_Dashboard(ServerIP="127.0.0.1", ServerPort=8000)
# server.StartServer() # blocks; run in a daemon thread for tests
client = General_User("127.0.0.1", 8000, user_id="alice")
client.ConnToServer("alice", 0x3B, filepath="myfile.txt") # 0x3B = sender / upload
client.ConnToServer("alice", 0x2B, filepath="myfile.txt") # 0x2B = receiver / downloadThe server ships with a small, production-appropriate image (stdlib-only, so no
pip install step is needed) that runs as a non-root user and binds
0.0.0.0:8000 inside the container.
docker build -t netforge .
docker run --rm -p 8000:8000 -v netforge-storage:/app/FileServer netforgeThe server is then reachable on the host at 127.0.0.1:8000 (point clients at
your host IP and port 8000). Uploaded/downloadable files and logs live under
/app/FileServer in the container, persisted in the netforge-storage volume.
docker compose up --build # add -d to run in the background
docker compose down # stop (the named volume is kept)Compose publishes 8000:8000 and mounts a named netforge-storage volume at
/app/FileServer so uploads and logs survive restarts. Connect clients to the
host at port 8000, for example:
python3 Client.py --host 127.0.0.1 --port 8000 --app sender --file ./myfile.txtThe handshake is a fixed sequence of 3-byte frames (each byte is a control code), followed by a null-terminated user ID and the file frame.
| Step | Direction | Size | Bytes / meaning |
|---|---|---|---|
| Frame 1 | client → server | 3 | connect(0xAA) | client(0x2A) | request(0x3C) |
| User ID | client → server | N+1 | utf-8 id + null terminator 0x00 |
| Frame 2 | server → client | 3 | accepted(0x6C) | server(0x0A) | client(0x2A) |
| Frame 3 | client → server | 3 | request(0x3C) | client(0x2A) | app 0x2B/0x3B |
| Frame 4 | server → client | 3 | accepted(0x6C) | server(0x0A) | app-echo |
| File frame | both | 8+N+8+size | !Q namelen | name | !Q size | raw bytes |
| Transfer status | server → client | 1 | 0x09 success | 0x10 failed |
| Hex | Meaning | Hex | Meaning |
|---|---|---|---|
0xAA |
connect | 0xFF |
disconnect |
0x0A |
server | 0x1A |
admin |
0x2A |
client | 0x2B |
receiver |
0x3B |
sender | 0x3C |
request |
0x6C |
accepted | 0x7C |
declined |
0x0C |
authorized access | 0x1C |
unauthorized access |
0x0D |
Connection established | 0x1D |
Connection lost |
0x09* |
File transfer successful | 0x10* |
File transfer failed |
* 0x09 / 0x10 are the file-transfer status bytes (see Client.client_control_codes);
the full 36-entry user_status_code table is printed by menu option 3 / dashboard.py --demo.
python3 -m pytest -qCurrently 22 tests pass (smoke imports, pure logic, report generation,
logging, framing round-trip via socketpair, loopback handshake integration,
and the dashboard --demo / EOF-safety tests).
NetForge/
├── dashboard.py # branded CLI launcher (interactive + --demo) <- start here
├── ServerDashboard.py # Server_Dashboard: accept loop + transfer dispatch (server entry point)
├── Client.py # General_User: reference client (client entry point)
├── ServerHandler.py # UserHandler: handshake validation state machine
├── ServerFunctions.py # Server_Functions: port scan, framing helpers, send/disconnect
├── ServerSettings.py # Server: sockets, control-code tables, shared state
├── ReportGenerator.py # Report_Generator: CSV / TXT / JSON reports
├── server_logs.py # Logs: per-named-logger file logging
├── requirements.txt
└── tests/ # pytest suite (test_netforge.py, test_dashboard.py)
- Python 3.8+. The server and client core use only the standard library.
- Optional:
rich(polished dashboard tables/panels — plain-ANSI fallback if absent) andpsutil(richer host stats — stdlib fallback otherwise). - Dev/testing:
pytest.
Install everything with pip install -r requirements.txt; the dashboard works
without the optional packages, just with a simpler look.