Final project for Advanced Programming course at Columbia University.
This project is a multi-client chat application written in C using IPv4 TCP sockets and POSIX APIs. A central server accepts concurrent connections and broadcasts each user's messages to the other connected clients. The client combines framed network communication with an ncurses interface for message entry, history, scrolling, status updates, and terminal resizing.
The repository pairs a completed networking client with the reference server and shared protocol utilities supplied for the course. This makes the full client/server protocol runnable locally while keeping the implementation boundary explicit.
flowchart LR
A[GUI Client A] -->|framed TCP messages| S[TCP Chat Server]
B[GUI Client B] -->|framed TCP messages| S
C[GUI Client C] -->|framed TCP messages| S
S -->|broadcast| A
S -->|broadcast| B
S -->|broadcast| C
The server uses select() to watch its listening socket and active client sockets. It accepts up to three simultaneous clients, associates each connection with a username, and broadcasts chat and presence messages. The client connects to a validated IPv4 address and port, receives a welcome message, sends its username, and then enters an event loop.
The client rebuilds an fd_set on every iteration and uses select() with a short timeout to monitor both STDIN_FILENO and the connected socket.
flowchart TD
L[Client event loop] --> S[select on STDIN and socket]
S -->|STDIN ready| I[Read GUI keystroke or redirected line]
I --> M[Frame and send message]
S -->|Socket ready| R[Receive one framed message]
R --> U[Update status and GUI history]
S -->|Timeout or signal interruption| L
M --> L
U --> L
This keeps terminal input and incoming network traffic responsive without dedicating a thread to either source.
- Creates IPv4 TCP sockets with
socket()and establishes client connections withconnect(). - Validates dot-decimal IPv4 addresses with
inet_pton()and checks command-line port and username constraints. - Frames every message with a 16-bit network-byte-order length through
send_with_length()andrecv_with_length(). - Multiplexes standard input and the client socket using
select(),fd_set,FD_SET, andFD_ISSET. - Supports interactive ncurses input as well as newline-delimited input redirected from a file.
- Integrates asynchronous messages into a fixed-capacity circular history queue used by the GUI.
- Tracks normal departure, server shutdown, connection loss, and
select()failure as explicit client states. - Uses
sigaction()for serverSIGINTshutdown and ignoresSIGPIPEin the client; the GUI also contains handlers forSIGINTandSIGWINCH, with a registration gap noted below. - Reports system-call failures with
errnoandstrerror()and releases sockets, allocated usernames, queue entries, and ncurses windows during normal cleanup paths. - Uses
SO_REUSEADDRon the server and rejects connections beyond its configured capacity.
- Preserving message boundaries over TCP, which exposes a byte stream rather than message-sized records.
- Responding to user input and network events concurrently without a threaded client.
- Detecting orderly shutdowns and unexpected peer disconnects from socket return values.
- Keeping message history and screen state consistent while messages arrive asynchronously or the terminal is resized.
- Integrating networking and lifecycle logic into an existing GUI-oriented C codebase.
- Managing file descriptors and heap-backed message data across connection and shutdown paths.
Repository history and the retained completion markers show that the project work focused on the client-side networking integration: argument validation, socket creation and connection setup, the welcome/username handshake, framed send/receive handling, select()-based input multiplexing, GUI message dispatch, redirected-input support, shutdown-state handling, welcome-message parsing, queue lifecycle calls, and the Makefile targets.
The ncurses GUI framework, circular queue implementation, shared protocol helpers in util.h, and chat server were provided as course infrastructure. The work involved completing and extending that supplied client skeleton rather than authoring every function from scratch.
The client requires a C compiler, Make, and ncurses development headers. On Debian or Ubuntu, the ncurses dependency can be installed with:
sudo apt install libncurses-devBuild both executables from the source directory:
cd src
makeTo remove generated binaries and object files:
make cleanStart the server with an available port in the supported range:
cd src
./chatserver 43127Then launch one or more clients in separate terminals:
cd src
./chatclientgui 127.0.0.1 43127 aliceUse a distinct username for each client. Messages sent by one client are relayed to the other connected clients.
Redirected standard input is also accepted:
./chatclientgui 127.0.0.1 43127 alice < messages.txtSee Known Limitations before relying on redirected-input shutdown behavior.
Client A ─┐
Client B ─┼→ TCP Server → Message broadcast
Client C ─┘
- C
- POSIX sockets and signals
- TCP/IP
select()-based I/O multiplexing- ncurses
- Make
- The client defines
SIGINTandSIGWINCHhandling but does not currently register those handlers, so those signal-driven client paths are incomplete. - After redirected standard input reaches
byeor end-of-file, the non-GUI client can remain in its event loop instead of terminating. - The supplied framing helpers assume each
send()orrecv()transfers the complete requested buffer; production code should loop to handle partial TCP I/O.
This project developed practical experience with TCP connection setup, file-descriptor lifecycle, length-prefixed protocols, and multiplexed I/O. It also required tracing state across signals, terminal events, socket disconnects, and an existing ncurses codebase—the same kinds of boundaries that make client/server debugging substantially different from debugging a standalone program.