A Python-based client-server networking project demonstrating TCP socket programming, concurrent client handling, and thread synchronization using Python's built-in socket and threading modules.
This project implements a TCP server capable of handling multiple clients simultaneously. Each connected client is assigned a dedicated worker thread, while a synchronization lock protects shared server resources from concurrent access.
Traditional single-threaded servers process one client at a time, which can cause other clients to wait while an active connection is being handled.
This project addresses that limitation by using a multi-threaded server architecture:
┌─────────────────────┐
│ TCP Server │
│ │
│ Listening Socket │
└──────────┬──────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Client 1 │ │ Client 2 │ │ Client 3 │
│ Thread-1 │ │ Thread-2 │ │ Thread-3 │
└───────────┘ └───────────┘ └───────────┘
│ │ │
└────────────────┼────────────────┘
▼
Shared Resources
│
Threading Lock
For every incoming connection, the server creates a new threading.Thread() instance. This allows multiple clients to communicate with the server concurrently without blocking one another.
- TCP-based client-server communication
- Multi-client concurrent connection handling
- Dedicated thread for every connected client
- Continuous two-way message exchange
- Graceful client disconnection
- Thread-safe access to shared resources
Lock.acquire()/Lock.release()synchronization- Client IP address and port tracking
- Active thread identification
- Clean separation between server and client implementations
- Built entirely with Python's standard library
multi-threaded-tcp-socket/
│
├── server.py
├── client.py
└── README.md
Responsible for:
- Creating the TCP listening socket
- Binding the server to a host and port
- Accepting incoming client connections
- Creating a dedicated thread for each client
- Receiving and responding to messages
- Displaying client network information
- Managing synchronized shared resources
- Handling client disconnections
Responsible for:
- Establishing a TCP connection with the server
- Continuously sending user messages
- Receiving server responses
- Maintaining the communication session
- Allowing the user to terminate the connection gracefully
| Technology | Purpose |
|---|---|
| Python 3 | Application development |
socket |
TCP network communication |
threading |
Concurrent client handling |
threading.Thread |
Dedicated worker thread per client |
threading.Lock |
Thread synchronization |
| TCP/IP | Reliable client-server communication |
No external Python packages are required.
The server creates a TCP socket using:
socket.socket(socket.AF_INET, socket.SOCK_STREAM)Where:
AF_INETspecifies IPv4 addressing.SOCK_STREAMspecifies TCP communication.
The socket is then bound to the configured host and port and placed into listening mode.
The server continuously waits for incoming connections:
client_socket, client_address = server_socket.accept()When a client connects, the server receives:
- Client socket
- Client IP address
- Client port number
The server then creates a new worker thread for that connection.
Conceptually:
Incoming Connection
│
▼
accept()
│
▼
Create Thread
│
▼
handle_client()
Each connected client is handled independently through a dedicated thread.
client_thread = threading.Thread(
target=handle_client,
args=(client_socket, client_address)
)
client_thread.start()This means the main server thread can immediately return to listening for additional connections while the newly created worker thread handles the client.
For example:
Main Server Thread
│
├── Client A → Thread-1
│
├── Client B → Thread-2
│
├── Client C → Thread-3
│
└── Client D → Thread-4
This architecture allows several clients to communicate with the server at the same time.
Because multiple worker threads can execute concurrently, shared resources may be accessed by more than one thread at the same time.
To prevent race conditions, the project uses Python's threading.Lock.
A shared resource can be protected using:
lock.acquire()
try:
# Access shared resource
...
finally:
lock.release()The lock ensures that only one thread enters the protected section at a time.
Thread A ──► acquire() ──► Critical Section ──► release()
│
Thread B ──► waits ──────────────────────────────┘
│
▼
Thread B acquires lock
This provides controlled access to shared server resources and reduces the possibility of inconsistent state caused by concurrent execution.
The client maintains an interactive communication loop.
The user can continuously:
- Enter a message.
- Send it to the server.
- Receive the server's response.
- Continue communicating.
- Exit when finished.
Conceptually:
User Input
│
▼
Client Socket
│
▼
TCP Network
│
▼
Server Worker Thread
│
▼
Server Response
│
▼
Client
│
└──────► Continue
The connection remains active until the user explicitly chooses to terminate the session.
For every active connection, the server displays useful connection information such as:
Thread Name
Client IP Address
Client Port Number
Example:
[Thread-1] Client connected: 127.0.0.1:54321
[Thread-2] Client connected: 127.0.0.1:54322
This makes the concurrent execution model visible during testing and demonstrates that different clients are being handled by separate threads.
Make sure Python 3 is installed:
python --versionor:
python3 --versionNo third-party dependencies are required.
Open a terminal and run:
python server.pyThe server will start listening for incoming TCP connections.
Example:
Server started on 127.0.0.1:5000
Waiting for connections...
Keep this terminal running.
Open another terminal:
python client.pyThe client will establish a TCP connection with the server.
To demonstrate concurrent execution, open additional terminals and run:
python client.pymultiple times.
For example:
Terminal 1 → Server
Terminal 2 → Client 1
Terminal 3 → Client 2
Terminal 4 → Client 3
The server should create a separate worker thread for each connected client.
A basic concurrency test can be performed by running several clients simultaneously.
Expected server-side behavior:
Server started...
Waiting for connections...
[Thread-1] Client connected: 127.0.0.1:xxxxx
[Thread-2] Client connected: 127.0.0.1:xxxxx
[Thread-3] Client connected: 127.0.0.1:xxxxx
Each client should be able to exchange messages independently while the other clients remain connected.
This demonstrates that the server is not restricted to processing a single client at a time.
Add screenshots demonstrating:
- Server running
- Multiple clients connected simultaneously
- Different thread names
- Client IP addresses and port numbers
- Successful message exchange
- Graceful client disconnection
The project uses a thread-per-client concurrency model.
TCP Server
│
accept connection
│
┌──────────┴──────────┐
│ │
Client 1 Client 2
│ │
Thread-1 Thread-2
│ │
▼ ▼
Message Loop Message Loop
│ │
└──────────┬──────────┘
│
Shared Resource
│
Threading Lock
The main server thread is responsible for accepting connections, while worker threads handle individual client sessions.
This separation allows the server to remain responsive to new connection requests.
When multiple threads operate concurrently, they may attempt to modify or access shared data at the same time.
For example:
Thread A ──┐
├──► Shared Resource
Thread B ──┤
│
Thread C ──┘
Without synchronization, simultaneous access can produce a race condition, where the final state depends on the unpredictable order in which threads execute.
Using a lock:
Thread A ──► LOCK ──► Shared Resource ──► UNLOCK
│
Thread B ─────────────────────────────────────┘
ensures controlled access to the critical section.
This project uses TCP (Transmission Control Protocol) rather than UDP.
TCP provides:
- Connection-oriented communication
- Reliable data delivery
- Ordered data transmission
- Error detection and retransmission
- Persistent communication between client and server
The communication flow is:
Client Server
│ │
│──── TCP Connection ─────────►│
│ │
│──── Message ────────────────►│
│ │
│◄──── Response ───────────────│
│ │
│──── Message ────────────────►│
│ │
│◄──── Response ───────────────│
│ │
│──── Disconnect ─────────────►│
│ │
This project demonstrates practical understanding of:
- TCP socket programming
- Client-server architecture
- IPv4 networking
- Python socket APIs
- Python multithreading
- Concurrent connection handling
- Thread lifecycle management
- Thread synchronization
- Mutual exclusion using locks
- Race-condition prevention
- Network debugging using IP addresses and ports
- Designing a continuously running network service
A basic sequential server can become blocked while communicating with one client.
Solution:
Create a dedicated thread for every client connection.
Multiple worker threads may access shared resources simultaneously.
Solution:
Use threading.Lock around critical sections.
Clients may disconnect unexpectedly or terminate their sessions.
Solution:
Handle connection termination gracefully and release associated resources.
The final demonstration should show that:
- The server starts successfully.
- Multiple clients can connect at the same time.
- Each client receives its own worker thread.
- Thread names are visible in the server terminal.
- Client IP addresses and port numbers are displayed.
- Multiple clients can exchange messages concurrently.
- Synchronization is performed using a lock.
- Clients can terminate their sessions without crashing the server.
Course: Parallel and Distributed Computing
Course Code: CSC-334
Lab: 03 — Socket Programming with Multi-Threading
The implementation focuses on applying concepts of concurrency, parallel execution, inter-thread synchronization, and network communication in a practical client-server environment.
This project provides a practical implementation of a concurrent TCP server using Python's standard networking and threading capabilities.
By assigning each client connection to an independent worker thread and protecting shared resources through synchronization locks, the system demonstrates the fundamental principles behind multi-threaded network services.
The project serves as a compact example of how socket programming and concurrency can be combined to build a responsive server capable of handling multiple clients simultaneously.
Zain
Computer Science / Software Engineering Student
This project was developed as part of the Parallel and Distributed Computing coursework and is structured to demonstrate practical implementation of TCP socket programming, multi-threading, and thread synchronization.


