Как сделать простой TCP сервер с «key-value» БД?

Несколько дней пытаюсь решить задание по взаимодействию клиент-сервер на C++.
Текст задания
На языке программирования C++ написать TCP сервер key-value БД, использую любую известную вам библиотеку или непосредственно используя socket api операционной системы Linux.
Сервер должен обрабатывать два типа запроса - на установку значения и на чтение значения клиентом.
Клиент устанавливает соединение на указанный порт и IP сервера (например 127.0.0.1:8080) и отправляет ключ/значение. Ключ может быть целым числом, значение в виде строки (к примеру [10, “Значение1”]). Предусмотреть вариант получения значения по ключу со стороны клиента. Ключи и значения можно хранить на сервере в оперативной памяти, например используя std::map.


У меня уже есть клиент-серверная архитектура, которая может установить соединение и отсылать текст из консоли клиента на сервер. Сервер в свою очередь обрабатывает этот текст и шлет обратно в консоль клиента.
Код сервера
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
    int sock, listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener < 0)
    {
        perror("socket");
        exit(1);
    }
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind");
        exit(2);
    }

    listen(listener, 1);
    
    while(1)
    {
        sock = accept(listener, NULL, NULL);
        if(sock < 0)
        {
            perror("accept");
            exit(3);
        }

        while(1)
        {
            bytes_read = recv(sock, buf, 1024, 0);
            if(bytes_read <= 0) break;
            send(sock, buf, bytes_read, 0);
        }
    
        close(sock);
    }
    
    return 0;
}

Код клиента
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

char message[] = "Hello there!\n";
char buf[sizeof(message)];

int main()
{
    int sock;
    struct sockaddr_in addr;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
        perror("socket");
        exit(1);
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(3425); // или любой другой порт...
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("connect");
        exit(2);
    }

    send(sock, message, sizeof(message), 0);
    recv(sock, buf, sizeof(message), 0);
    
    printf(buf);
    close(sock);

    return 0;
}


Пришел к тому, что как вариант использовать встраиваемую БД SQLite. Ещё как вариант нашел "leveldb"...
Не знаю как ее встроить в это клиент-серверное приложение, чтобы выполнить поставленную задачу. Как именно реализовать запись и чтение пар "ключ-значение" в TCP приложении?

Подскажите пожалуйста, куда копать и где можно почитать?
  • Вопрос задан
  • 258 просмотров
Решения вопроса 1
@K0lya28
Получение и Изменение
1)Через map.find() найти пару в std::map и получить итератор
2)Если iterator == map::end(), ключа нет
3)Отослать или изменить iterator->second

Добавление
1)map.insert(make_pair(ключ, значение))
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
firedragon
@firedragon
Не джун-мидл-сеньор, а трус-балбес-бывалый.
Своруйте с редиса
Ответ написан
@KupaKupychOfGenius
Ээээ, как бы в задание написано как сделать хранилище:
Ключи и значения можно хранить на сервере в оперативной памяти, например используя std::map.
Но для начала придется ведь придумать протокол общения клиента и сервера, например, строка вида
'set 10 значение1' - установка значения с конкретным ключём
'get 10' - получение значения по ключу 10

int main()
{
    int sock, listener;
    struct sockaddr_in addr;
    char buf[1024];
    int bytes_read;

     //std::map - это контейнер типа key-value, он же хэш в простонародие, он же dictionary, он же словарь
     //вот его мы и объявляем, ключ у него целого типа, а значени - строка
     std::map<unsigned int,std::string>  storage;   
     std::string input_string;   // полученная строка 
     std::string operation;     
     std::string key_as_string;
     std::string value;
     unsigned int key_as_int;            
     ....

 while(1)
        {
            bytes_read = recv(sock, buf, 1024, 0);
            if(bytes_read <= 0) break;
            
             //c std::string работать удобнее
            input_string = buf;
            
            //эту функцию вы уж как-нибудь сами напишите(своруйте с интерентов), в целом такая операция называется split
            parse_input_string(input_string,&operation, &key_as_string,&value); 
            key_as_int = stoi(key_as_string);

             // пришел запрос на получение значения
             if (operation == "get") {
            
                 //ищем ключ, в случае успеха метод find возратит нам итератор на элемент с нашим ключем
                //в противном случае иетратор на элемент заглушку в конце контейнера
                auto it = storage.find(key);  
               //если ключ не найден
               if (it == storage.end()) {        
                   buf = "not found";
               else {
                   strcpy(buf, iterator->second.c_str() );   // получим значение и скопируем его в буфер для отправки клиенту
               }
           } else if (operator == "set")  {
                 // если ключа еще нет, появится новая пара, если есть, то текуще значение затрется новым
                storage[key_as_int] = value;  
                buf = "saved successfully";
           }
            send(sock, buf, bytes_read, 0);
        }


Код набросал схематичный, проверки опущены, нужные заголовочные файлы тоже, на работспособность не проверял.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы