Могу ли я написать в закрытый сокет и принудительно исправить ошибку сломанной трубы?

У меня есть приложение, которое работает на большом количестве процессоров. На процессоре 0 у меня есть функция, которая записывает данные в сокет, если он открыт. Эта функция выполняется в цикле в отдельном потоке на процессоре 0, т. е. процессор 0 отвечает за свою собственную рабочую нагрузку и имеет дополнительный поток, обеспечивающий связь на сокете.

//This function runs on a loop, called every 1.5 seconds
void T_main_loop(const int& client_socket_id, bool* exit_flag)
{
    //Check that socket still connected.
    int error_code;
    socklen_t error_code_size = sizeof(error_code);
    getsockopt(client_socket_id, SOL_SOCKET, SO_ERROR, &error_code, &error_code_size);

    if (error_code == 0)
    {
        //send some data
        int valsend = send(client_socket_id , data , size_of_data , 0);
    }
    else
    {
        *(exit_flag) = false; //This is used for some external logic.
        //Can I fix the broklen pipe here somehow?
    }
}

Когда клиентский сокет закрывается, программа должна просто игнорировать ошибку, и это стандартное поведение, насколько мне известно.

Однако я использую внешнюю библиотеку (PETSc), которая каким-то образом обнаруживает ошибку сломанной трубы и закрывает всю параллельную (MPI) среду:

[0]PETSC ERROR: Caught signal number 13 Broken Pipe: Likely while reading or writing to a socket

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


person wvn    schedule 14.01.2020    source источник
comment
Вы не можете заставить разорванное соединение вернуться в рабочее состояние, нет.   -  person Jesper Juhl    schedule 14.01.2020
comment
Почему вы используете SO_ERROR для проверки состояния подключения? Вы вообще не должны этого делать. Просто вызовите send() безоговорочно, и если это не удастся, errno сообщит вам, почему, например EBADF/ENOTSOCK, ECONNRESET, ENOTCONN, EPIPE и т. д. Вместо этого используйте это для обнаружения разрыва соединения.   -  person Remy Lebeau    schedule 16.01.2020
comment
@RemyLebeau Приложение предназначено для закрытия соединения при любой ошибке и последующего повторного открытия нового соединения на том же порту сразу же после этого. Реализация достаточно проста, так что фактический код ошибки не имеет значения. Я не уверен, что это не должно быть сделано таким образом, вы могли бы уточнить?   -  person wvn    schedule 16.01.2020
comment
@wvn void T_main_loop(const int& client_socket_id, bool* exit_flag) { int valsend = send(client_socket_id, data, size_of_data, MSG_NOSIGNAL); if (valsend < 0) { /* check errno if needed */ *exit_flag = false; } }   -  person Remy Lebeau    schedule 16.01.2020
comment
@RemyLebeau Это, конечно, работает, но в чем преимущество, кроме большей краткости?   -  person wvn    schedule 16.01.2020
comment
@wvn с SO_ERROR, вы не знаете, какая предыдущая операция не удалась или когда она не удалась. И вам не нужно знать. При таком подходе вы хотите отправить данные СЕЙЧАС, а СЕЙЧАС это не удается, поэтому действуйте независимо от того, что произошло ДО этого.   -  person Remy Lebeau    schedule 16.01.2020


Ответы (1)


По умолчанию ОС отправляет поток SIGPIPE, если пытается записать в (наполовину) закрытый канал или сокет.

Одним из вариантов отключения сигнала является выполнение signal(SIGPIPE, SIG_IGN);.

Другой вариант - использовать флаг MSG_NOSIGNAL для send, например. send(..., MSG_NOSIGNAL);.

person Maxim Egorushkin    schedule 14.01.2020
comment
Является ли второй вариант более безопасным в случае, если эта библиотека использует сигнал? - person wvn; 14.01.2020
comment
@wvn Точно. При использовании второго метода вы не изменяете глобальное состояние процесса. - person Maxim Egorushkin; 14.01.2020