libmpdclient: обнаружить потерянное соединение с демоном MPD

Я пишу плагин для своей строки состояния для печати состояния MPD, в настоящее время использую библиотеку libmpdclient. Он должен быть надежным, чтобы правильно обрабатывать потерянные соединения в случае перезапуска MPD, но простая проверка с помощью mpd_connection_get_error на существующих Объект mpd_connection не работает, он может обнаружить ошибку только в случае сбоя исходного mpd_connection_new.

Это упрощенный код, с которым я работаю:

#include <stdio.h>
#include <unistd.h>
#include <mpd/client.h>

int main(void) {
    struct mpd_connection* m_connection = NULL;
    struct mpd_status* m_status = NULL;
    char* m_state_str;

    m_connection = mpd_connection_new(NULL, 0, 30000);

    while (1) {
        // this check works only on start up (i.e. when mpd_connection_new failed),
        // not when the connection is lost later
        if (mpd_connection_get_error(m_connection) != MPD_ERROR_SUCCESS) {
            fprintf(stderr, "Could not connect to MPD: %s\n", mpd_connection_get_error_message(m_connection));
            mpd_connection_free(m_connection);
            m_connection = NULL;
        }

        m_status = mpd_run_status(m_connection);
        if (mpd_status_get_state(m_status) == MPD_STATE_PLAY) {
            m_state_str = "playing";
        } else if (mpd_status_get_state(m_status) == MPD_STATE_STOP) {
            m_state_str = "stopped";
        } else if (mpd_status_get_state(m_status) == MPD_STATE_PAUSE) {
            m_state_str = "paused";
        } else {
            m_state_str = "unknown";
        }
        printf("MPD state: %s\n", m_state_str);
        sleep(1);
    }
}

Когда MPD останавливается во время выполнения вышеуказанной программы, он выдает ошибку segfault:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007fb2fd9557e0 in mpd_status_get_state () from /usr/lib/libmpdclient.so.2

Единственный способ, который я могу придумать, чтобы сделать программу безопасной, — это устанавливать новое соединение на каждой итерации, чего я надеялся избежать. Но что делать, если связь между отдельными вызовами libmpdclient функций теряется? Как часто и, что более важно как именно, я должен проверять, живо ли соединение?


c mpd
person Jakub Klinkovský    schedule 06.01.2015    source источник


Ответы (2)


Единственный способ, который действительно работает (помимо восстановления соединения при каждом запуске), — это использование команда бездействия. Если mpd_recv_idle (или mpd_run_idle) возвращает 0, это состояние ошибки, и вы можете принять это как сигнал, чтобы освободить соединение и запустить оттуда. Это не идеальное решение, но оно позволяет поддерживать живое соединение между запусками и помогает избежать segfaults (хотя я не думаю, что вы можете полностью избежать их, потому что если вы отправляете команду, а mpd уничтожается до того, как вы получите это, я почти уверен, что библиотека все еще segfaults). Я не уверен, что есть лучшее решение. Было бы замечательно, если бы был надежный способ определить, активно ли ваше соединение через API, но я не могу найти ничего подобного. Не похоже, что libmpdclient хорошо сконструирован для очень долгоживущих соединений, которым приходится иметь дело с экземплярами mpd, которые со временем увеличиваются и уменьшаются.

Другой вариант более низкого уровня — использовать сокеты для взаимодействия с MPD через его протокол напрямую, хотя и в делая это, вы, вероятно, в любом случае переопределите большую часть самого libmpdclient.

РЕДАКТИРОВАТЬ: К сожалению, команда idle блокируется до тех пор, пока что-то не произойдет, и может блокироваться до тех пор, пока длится одна звуковая дорожка, поэтому, если вам нужно, чтобы ваша программа выполняла другие действия в промежутке времени, вам нужно найти способ реализовать это асинхронно или в другом потоке.

person Taywee    schedule 23.11.2015

Предполагая, что conn — это соединение, созданное с помощью mpd_connection_new:

if (mpd_connection_get_error(conn) == MPD_ERROR_CLOSED) {
    // mpd_connection_get_error_message(conn)
    // will return "Connection closed by the server"
}

Вы можете запустить эту проверку практически после любого вызова libmpdclient, включая mpd_recv_idle или (как в вашем примере) mpd_run_status.

Я использую libmpdclient 2.18, и это определенно работает для меня.

person user240515    schedule 29.06.2020