Skip to content

Jiahong-Guan/Simple-TCP-Connection

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simple TCP Connection – Simple Multi-Client Server & Client (Python3)

"A tiny, pure‑Python, object‑oriented implementation of a TCP chat system that works out‑of‑the‑box on any platform with Python 3.8+. The server only shuttles bytes (it never interprets the payload) – it just relays every line a client sends to all the other connected clients. The client is a small console chat program that can be run in multiple terminals simultaneously."



Table of Contents


Features

Server Client
✅ Handles multiple concurent clients (one thread per client) ✅ Interactive REPL – type messages, see incoming traffic live
✅ First line from a client is treated as nickname ✅ Clean shutdown via Ctrl-C, /quit or /exit
Broadcast: every line is forwarded unchanged to all other clients ✅ No external dependencies (standard-library only)
Thread-safe client list – uses a lock without nested acquisition (no dead-lock) ✅ Argument parsing (--host, --port, nickname)
✅ Graceful handling of broken sockets & client disconnects ✅ Signals (SIGINT) are caught for a tidy exit
✅ MIT-Licensed – completely free to use, modify and embed ✅ Compatible with Python 3.8+ (type-hints, f-strings)


Requirements

Component Minimum version
Python 3.8 (tested on 3.8-3.12)
Operating System Any (Linux, macOS, Windows) – no OS‑specific code

"No third‑party packages are required. All imports come from the Python standard library (socket, threading, argparse, signal, …)."



Installation

  1. Clone (or download) the repository:

       git clone https://github.com/Jiahong-Guan/Simple-TCP-Connection.git
       cd Simple-TCP-Connection
  2. (Optional) Create a virtual environment:

    python -m venv .venv
    source .venv/bin/activate      # macOS/Linux
    .venv\Scripts\activate.bat     # Windows
  3. Verify the interpreter:

    python --version   # should show 3.8 or higher


Usage

Start the Server

# Default: listen on all interfaces (0.0.0.0) and port 55000
python server.py

# Override host / port
python server.py --host 127.0.0.1 --port 9000

The server will print a line such as:

[+] Server listening on 0.0.0.0:55000

"Note: If you run the server on a remote machine, make sure the port is allowed through any firewall."

Connect a Client

# Connect to the default server (127.0.0.1:55000) with nickname "Alice"
python client.py Alice

# Connect to a remote host / custom port
python client.py --host 10.1.2.3 --port 9000 Bob

When the connection succeeds you will see something like:

[+] Connected to 127.0.0.1:55000 as Alice
Type your messages and press <Enter>. /quit or /exit to leave.

Command-Line Options

Both programs share the same flags:

Flag Description Server default Client default
-H, --host IP address / hostname to bind (server) or connect to (client) 0.0.0.0 (listen on all) `127.0.0.1 (localhost)
-p, --port TCP port number 55000 55000
positional nickname (client only) Display name that is sent as the first line to the server required

You can view the help for each script:

python server.py -h
python client.py -h

Typical Chat Session

# Terminal 1 – server
$ python server.py
[+] Server listening on 0.0.0.0:55000
[+] New connection from ('127.0.0.1', 61858) as Alice
[+] New connection from ('127.0.0.1', 61860) as Bob

# Terminal 2 – client Alice
$ python client.py Alice
[+] Connected to 127.0.0.1:55000 as Alice
Type your messages and press <Enter>. /quit or /exit to leave.
[Bob] entered the chat.
Alice> Hi Bob!
[Bob] Hi Alice!
Bob> Hey Alice! How are you?
Alice> I'm fine, thanks!
...

# Terminal 3 – client Bob
$ python client.py Bob
[+] Connected to 127.0.0.1:55000 as Bob
Type your messages and press <Enter>. /quit or /exit to leave.
[Bob] entered the chat.
[Alice] Hi Bob!
...

# Bob quits
Bob> /quit
[+] Caught interrupt, exiting …
[-] ('127.0.0.1', 61860) (Bob) disconnected
[Alice] Bob left the chat.

The server stays alive and can accept new clients at any time.



Design & Implementation Notes

  1. Thread-Safe Client List

    Problem: The original version nested **with self._clients_lock** inside _broadcast, leading to a deadlock.

    Solution: The fixed server snapshot-and-send pattern:

    # Take snapshot while holding the lock
    with self._clients_lock:
        recipients = [(sock, nick) for sock, nick in self._clients if sock is not source]
    
    # Send to each socket *outside* the lock
    for sock, _ in recipients:
        sock.sendall(message.encode("utf-8"))

    No lock is held while performing network I/O, eliminating deadlocks and keeping the server responsive even if a client is slow.

  2. Clean Separation of Concerns

    Both Server and Client are fully encapsulated in classes exposing only start()/connect()/shutdown() (or __enter__/__exit__).

    All heavy work lives in private methods (_handle_client, _receive_loop, _input_loop).

  3. Argparse-Driven CLI

    The scripts use argparse with short (-H) and long (--host) options, default values and helpful -h/--help output. The same flags work for both server and client, keeping the user experience consistent.

  4. Signal Handling

    signal.signal(signal.SIGINT, ...) installs a handler that sets a shutdown event and triggers a graceful cleanup. The client installs the handler once in connect(); the server does the same (start()). This avoids multiple registrations when the class is instantiated multiple times.

  5. Cross-Platform Compatibility

    All socket calls use IPv4 (AF_INET) and plain TCP (SOCK_STREAM). No platform-specific APIs are used, ensuring the code runs unchanged on Windows, macOS, and Linux.

  6. Minimal Dependencies

    Only the Python standard library is required; therefore the project can be packaged as a single-file utility or bundled into a zipapp if desired.



Testing & Troubleshooting

Situation Command / Check Expected Result
Server refuses to bind python server.py --port 80 (or any privileged port) OSError: [Errno 13] Permission denied – use a port ≥ 1024.
Client cannot connect python client.py --host 192.0.2.1 (non-existent) RuntimeError: Unable to connect... – verify server is reachable and firewall allows the port.
Message not propagating Send a line from Alice, nothing appears for Bob Check server stout for "[+] New connection..." and "[Bob] entered...". If the server shows the broadcast, the problem is on the client side (e.g., client blocked on input()).
Server hangs after a client leaves After Alice quits, the server UI freezes This was caused by the nested lock in the original code – the fixed version does not lock while broadcasting, so the server stays responsive.
Ctrl-C leaves a stray thread Press Ctrl-C in a client → process doesn’t exit Ensure you are running the latest client.py. The shutdown event is set before the socket is closed, causing both the receiving and input threads to terminate.

If you encounter any other odd behaviour, feel free to open an issue (see Contributing below).

Contributing

Contributions, bug reports, and suggestions are very welcome!

  1. Fork the repository.
  2. Create a branch for your feature or fix: git checkout -b my-new-feature.
  3. Make your changes, keeping PEP-8 style (you can run flake8 or black locally).
  4. Write tests (if you add new behaviour). The code is deliberately tiny, so a simple functional test that spins up a server in a thread and a client in another thread is enough.
  5. Submit a Pull Request with a clear description of what you changed.

When submitting a PR, please:

  • Keep the public API (class names, method signatures) unchanged unless you also update the documentation.
  • Add or update the README if you introduce new command-line options or behaviours.
  • Ensure the project still runs with the default Python on all major OSes (you can use GitHub Actions or a local VM for verification).


License (MIT)

MIT License

Copyright (c) 2025 Jiahong

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

The full license text is also provided in the LICENSE file included in the repository.


About

Simple multi‑client TCP chat application written in pure Python 3. It includes a server that relays messages and a console client, both built with the standard library socket, threading, and an object oriented design. MIT licensed.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages