Kelick Tech Hub

Build, learn & keep up — tech, Linux & sports news included.

C++ Simple Chat App (Console) Project

Intermediate•2–3 days•sockets, threads, networking

Two terminals on one network, sending messages to each other in real time.

Share on X

What you will build

A chat program where one side runs as the server and the other connects as a client, then messages flow both ways at once — which means one thread must listen while the main thread types. It's a first real taste of network programming and concurrency together.

What you will learn

Feature checklist

Starter code

// POSIX sockets (Linux/macOS). Server side sketch:
#include <iostream>
#include <string>
#include <thread>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

void receiveLoop(int sock) {
    char buf[1024];
    ssize_t n;
    while ((n = recv(sock, buf, sizeof(buf) - 1, 0)) > 0) {
        buf[n] = '\0';
        std::cout << "\nPeer: " << buf << "\nYou: " << std::flush;
    }
    std::cout << "\nPeer disconnected.\n";
    std::exit(0);
}

int main() {
    int server = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in addr{AF_INET, htons(54000), INADDR_ANY};
    bind(server, (sockaddr*)&addr, sizeof(addr));
    listen(server, 1);
    std::cout << "Waiting for a friend on port 54000...\n";
    int client = accept(server, nullptr, nullptr);

    std::thread t(receiveLoop, client);
    t.detach();
    std::string line;
    std::cout << "You: ";
    while (std::getline(std::cin, line))
        send(client, line.c_str(), line.size(), 0);
}

Full walkthrough

Step 1: Understand the shape: one program, two modes

Run with 'server' and it listens; run with 'client <ip>' and it connects. Both modes then do the same thing — send what you type, print what arrives — which is why the receive logic is shared. If the two-terminal demo works on one machine (127.0.0.1), the networking is right.

Step 2: Create and bind the server socket

socket(AF_INET, SOCK_STREAM, 0) creates a TCP endpoint. Fill a sockaddr_in with INADDR_ANY and htons(5555), bind() it, listen(), then accept() — accept BLOCKS until a client connects, and returns a NEW socket for that client. The listening socket and the client socket are different things; confusing them is the classic first-week bug.

Step 3: Connect from the client

The client creates its socket and connect()s to the server's address (127.0.0.1 for the demo, a real IP over a network). From here both sides hold one socket each and the code can be symmetric — which is exactly why the shared receive loop works.

Step 4: Run receiving on its own thread

Receiving blocks, and typing blocks; one thread cannot wait for both. So receiveLoop(int sock) runs in a std::thread: it recv()s into a buffer and prints 'name: message' lines until recv returns 0 (the other side quit), while main() loops on std::getline sending your lines with send(). This two-thread split is THE pattern of every chat program.

void receiveLoop(int sock) {
    char buf[1024];
    while (true) {
        int n = recv(sock, buf, sizeof(buf) - 1, 0);
        if (n <= 0) { std::cout << "\n(peer left)\n"; break; }
        buf[n] = '\0';
        std::cout << buf << std::endl;
    }
}

Step 5: Prefix messages with the username

On connect, each side sends its name once; every chat line is sent as 'name: text' (or the receiver prepends the known peer name). In a two-person chat the name is cosmetic — in the advanced multi-client version it becomes essential routing information.

Step 6: Handle quitting cleanly

'/quit' breaks the send loop, then close(sock) and join the receive thread. On the receiving side, recv() returning 0 means an orderly shutdown — print '(peer left)' and exit rather than spinning on empty reads. A chat that requires Ctrl+C to leave will also leave sockets and threads dangling.

Step 7: Test with two terminals on one machine

Terminal 1: ./chat server. Terminal 2: ./chat client 127.0.0.1. Type both ways, confirm usernames, then /quit from one side and watch the other announce the departure. Only after localhost works should you try a real second machine — most 'network bugs' are firewall and IP issues, not code.

Common mistakes

Compile and run

g++ -std=c++17 -o chat chat.cpp -pthread

POSIX sockets: this builds on Linux and macOS as-is. On Windows, sockets live in Winsock (winsock2.h, link -lws2_32) and the calls differ — most learners build this one in WSL or a Linux VM. The -pthread flag is needed for the std::thread receive loop.

Sample run

$ ./chat server
Server listening on port 5555...
Client connected.
ada: hello, can you see this?
> yes! loud and clear
ada: the two-thread receive loop works
ada: try /quit when ready
> /quit
(you left the chat)

——— second terminal ———
$ ./chat client 127.0.0.1
Connected to server. Your name:
> ada
you: yes! loud and clear
ada: the two-thread receive loop works
(peer left)

Try it in your browser

No browser demo is included for this project. Build the C++ version below — that is the real program.

Related Intermediate projects

← All 60 projects · Classic site (Learn & Sandbox)

Download Fake GPS