mini_db

Código comentado línea a línea y paso a paso:

Entregas: mini_db.hpp, mini_db.cpp. Flags: c++ -Wall -Wextra -Werror (compila en C++98).

Qué pide (en palabras normales)

Subject (enunciado original)
Assignment name: mini_db
Expected files : *. cpp *. hpp
-----------------------------------------------------------------

Write a program that takes as an argument a port and a path.
Your program must listen for clients to connect on 127.0.0.1 on the given port.

When your program is ready to accept incoming connections, it will print "ready\n" on
the standard output.

Your program will receive commands from the connected clients and will maintain a
database of key-values.

The connections are persistent, which means that a program can send multiple
commands in a single session.

The commands are the following:

POST:
synopsis:		POST <key> <value>
description:	add an entry to the db
return value:	"0"

GET:
synopsis:		GET <key>
description:	retrieve an entry from the db
return value:	"0 <value>" or "1" if the key does not exist

DELETE:
synopsis:		DELETE <key>
description:	erase an entry in the db
return value:	return "0" if the key was successfully erased or "1" if the key does not exist

If a command is not recognized your program must respond "2".

Keys and values will be of arbitrary size and will not contain any white spaces,
but the size of a request will never exceed 1000 characters.

Your program must be efficient.

Data persistence:

When your program receives the SIGINT signal, it must save the current database (i.e. all the
key-value pairs) to the file path given as an argument and exit.

Each time your program starts, it will look for the presence of this file
and load it.

Hint: you can use nc to test your program.

You will find in this directory a file containing some network code in case you
have forgotten how it works.

Example:

$> ./mini_db 1111 .save &
$> nc localhost 1111 | cat -e
POST A B
O$
POST B C
0$
GET A
0 B$
GET C
1$
DELETE A
O$
DELETE C
1$
UNKNOWN_COMMAND
2$
^C
$> pkill -SIGINT mini_db
$> ./mini_db 1111 .save &
$> echo "GET B" | nc localhost 1111 | cat -e
0 C$
S>

4 piezas

1. La base de datos es un map y dos streams

while (in >> key >> value)		// load
	_data[key] = value;
out << it->first << ' ' << it->second << '\n';	// save

Sin espacios dentro de clave ni valor: >> parte por espacios y basta. El fichero es clave valor por línea, lo que load lee tal cual.

2. Un comando → una respuesta

ss >> cmd >> key >> value >> extra;
if (cmd == "POST" && !value.empty() && extra.empty()) ...
if (cmd == "GET" && !key.empty() && value.empty()) ...

Leo hasta 4 palabras: las que falten quedan vacías. Así compruebo a la vez el comando y su número de argumentos. Lo que no cuadre, 2. run no sabe de sockets: se prueba con un string.

3. SIGINT con bandera, no con trabajo dentro del handler

static volatile sig_atomic_t g_stop = 0;
static void on_sigint(int) { g_stop = 1; }
...
while (!g_stop) { select(...); ... }
db.save();

La señal interrumpe select (devuelve -1), el bucle mira la bandera y sale, y save corre con calma fuera del handler. Dentro de un handler no es seguro usar streams ni malloc.

4. Varios comandos por conexión

bufs[fd].append(buf, n);
while ((nl = bufs[fd].find('\n')) != std::string::npos)
{
	reply = db.run(bufs[fd].substr(0, nl));
	bufs[fd].erase(0, nl + 1);
	send(fd, reply.c_str(), reply.size(), 0);
}

Un std::string por cliente acumula lo recibido; cada \n es un comando. La respuesta va solo al que preguntó (no es un chat).

mini_db función por función

FunciónQué hace
Db::load()fichero → map con >>
Db::save()map → fichero, clave valor por línea
Db::run(line)un comando → su respuesta con \n
on_sigintlevanta g_stop
listen_on(port)socket, SO_REUSEADDR, bind 127.0.0.1, listen
serve(sock, db)bucle select: accept, recv, run, send, hasta g_stop
mainargumentos, load, signal, listen, ready, serve, save

Trampas

Otras soluciones

No hay otras publicadas que conozca: ni stc71 ni mohaben-1 tienen mini_db. El simulador 42_examshell trae una de referencia (C++11, guarda dentro del handler y no parte por \n).