Building a Distributed Key-Value Store in C++ (Part 3)
- Wrapping the KVStore in a TCP server is mostly boilerplate — socket, bind, listen, accept, thread-per-connection.
- A text-based newline-delimited protocol is good enough for a first pass and easy to debug with netcat.
- Networking code is best validated with manual integration tests before introducing a test harness for socket I/O.
Parts 1 and 2 gave us a persistent, tested in-process store. Part 3 wraps it in a TCP server so any client — a shell script, another process, or the CLI client below — can talk to it.
The KVServer class
// server.hpp
class KVServer {
public:
KVServer(int port, const std::string& store_file = "logs/store.log");
void run();
private:
int port;
KVStore store;
void handle_client(int client_socket);
};run() opens a listening socket and spawns a detached thread for every incoming connection. This is a naive thread-per-connection model — fine for a learning project, not suitable for production.
Main server loop
void KVServer::run() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// ... bind, listen
while (true) {
int client_socket = accept(server_fd, ...);
std::thread(&KVServer::handle_client, this, client_socket).detach();
}
}Command handling
void KVServer::handle_client(int client_socket) {
char buffer[1024] = {0};
while (true) {
int valread = read(client_socket, buffer, sizeof(buffer) - 1);
if (valread <= 0) break;
std::istringstream iss(buffer);
std::string cmd, key, value;
iss >> cmd >> key;
std::ostringstream response;
if (cmd == "PUT") {
iss >> value;
store.put(key, value);
response << "OK\n";
} else if (cmd == "GET") {
auto val = store.get(key);
response << (val ? *val : "NOT_FOUND") << "\n";
} else if (cmd == "DEL") {
response << (store.del(key) ? "OK\n" : "NOT_FOUND\n");
} else if (cmd == "QUIT") {
break;
} else {
response << "ERROR UNKNOWN COMMAND\n";
}
send(client_socket, response.str().c_str(), response.str().size(), 0);
}
close(client_socket);
}All socket I/O in this implementation is integration-tested manually. Automating socket tests requires a test harness that spins up and tears down the server in-process, which is left for a future refactor.
The CLI client
The client connects to an IP and port, then enters a read-send-print loop:
while (std::cout << "> ", std::getline(std::cin, input)) {
send(sock, input.c_str(), input.length(), 0);
char buffer[1024] = {0};
int valread = read(sock, buffer, sizeof(buffer)-1);
if (valread > 0) std::cout << buffer;
if (input == "QUIT") break;
}An interactive session
$ ./kvstore_server
KVServer listening port 12345...
$ ./kvstore_client 127.0.0.1 12345
> PUT hello world
OK
> GET hello
world
> DEL hello
OK
> GET hello
NOT_FOUND
> QUITCMake: two executables from one library
add_executable(kvstore_server src/server.cpp src/server.hpp src/server_main.cpp)
target_link_libraries(kvstore_server PRIVATE kvstore_lib)
add_executable(kvstore_client src/client.cpp)
target_link_libraries(kvstore_client PRIVATE kvstore_lib)Splitting the store logic into kvstore_lib means both executables share the same compiled translation units and tests link against the same target.
Roadmap
- Phase 1 — Local store — done
- Phase 2 — Persistence & testing — done
- Phase 3 — Networking — done (this post)
- Phase 4 — Multi-node architecture — next
- Phase 5 — Consensus / leader election — planned
- Phase 6 — Testing & resilience — planned
What's next
Part 4 runs multiple server instances and replicates writes between them — the first real step into distributed territory.
…and the overall roadmap. Parts [2](/notes/building-distributed-kv-store-pt2), [3](/notes/building-distributed-kv-store-pt3), and [4](/notes/building-distributed-kv-store-pt4) added persistence, TCP netwo…
…le mess as the codebase grows. </KeyTakeaways> With a working TCP server from [Part 3](/notes/building-distributed-kv-store-pt3), the next step is running multiple instances and keeping them in sync. This par…
…ion** — planned - **Phase 6 — Testing & resilience** — planned ## What's next [Part 3](/notes/building-distributed-kv-store-pt3) puts the store on the network. A `KVServer` will accept TCP connections and han…

Data is my veggies — healthy, versatile, and sometimes hard to digest, but in the end, it always brings value.