Nano Hash - криптовалюты, майнинг, программирование

Использование функции записи POSIX после отключения моего соединения TCP/IP приводит к сбою моего приложения - почему?

Я работаю над приложением C, которое использует функции POSIX TCP/IP для связи с сервером. В настоящее время я провожу некоторые тесты, чтобы увидеть, как приложение реагирует, когда соединение неожиданно закрывается.

Основная функция работного дома показана ниже:

uint32_t netWriteMsg(uint8_t * pmsg, size_t msg_size)
{
    if(write(m_sockfd, pmsg, msg_size) < msg_size)
        return ERR_NET_NOT_ALL_BYTES_SENT;

    return ERR_NONE;
}

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

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

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

Вот как я настроил свой сокет:

uint32_t netConnect()
{
    /* locals */
    struct sockaddr_in serv_addr;
    fd_set fdset_sock; // only 1 file descriptor (socket fd) will be placed in this set
    fd_set fdset_empty;
    struct timeval time = {NET_TIMEOUT_CONNECT, 0}; 
    int sock_error;
    socklen_t optlen;
    int error = ERR_NONE;

    /* obtain socket file descriptor and set it to non-blocking */
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT_NO);
    inet_pton(AF_INET, IP_ADDR, &(serv_addr.sin_addr.s_addr));


    /* attempt to connect */
    error = connect(m_sockfd, &serv_addr, sizeof(serv_addr));
    if(error) return ERR_NET_CONNECT_FAILED_IMMEDIATELY;

    select(m_sockfd, &fdset_empty, &fdset_sock, &fdset_empty, &time); // blocks until socket is good or timeout occured
    error = getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &sock_error, &optlen);
    if(error) return ERR_NET_COULD_NOT_GET_SOCKET_OPTION;

    if(sock_error)
        return ERR_NET_CONNECT_ATTEMPT_TIMEOUT; 

    m_is_connected = 1;

    return ERR_NONE;        
}

Любая помощь будет оценена

01.05.2017

  • Если для вызова записи нет получателя, ваш процесс должен быть уничтожен SIGPIPE. Установите расположение этого сигнала на SIG_IG, и вы должны начать получать ошибку EPIPE errno вместо сигнала. 02.05.2017
  • Вы вообще не выполняете проверку ошибок на socket() или select(), и ваша проверка ошибок на connect() неверна. Если m_sockfd не блокирует, connect() вернет -1, а errno будет EINPROGRESS. Вам нужно проверить это перед возвратом ERR_NET_CONNECT_FAILED_IMMEDIATELY. И получение SO_ERROR допустимо только в том случае, если есть код ошибки для извлечения, поэтому убедитесь, что select() выполняется успешно, прежде чем вызывать getsockopt(). 02.05.2017
  • @PSkocik: другие варианты включают 1) использование send() вместо write(), чтобы вы могли указать флаг MSG_NOSIGNAL, и/или 2) использование setsockopt() для включения параметра SO_NOSIGPIPE. На платформах, поддерживающих эти опции, т.е. 02.05.2017

Ответы:


1

В дополнение к отсутствующей проверке ошибок, упомянутой @RemyLebeau, вы также не проверяете ошибки самого write():

if(write(m_sockfd, pmsg, msg_size) < msg_size)
    return ERR_NET_NOT_ALL_BYTES_SENT;

Здесь вы игнорируете возможность того, что он вернул -1, и в этом случае вы должны вызвать perror() или создать строку сообщения об ошибке с strerror() и напечатать ее, и закрыть сокет, и > скажите звонящему, чтобы он не продолжал писать.

Вам также необходимо установить для SIGPIPE значение SIG_IGNORE или что-то еще, чтобы ошибки записи EPIPE не вызывали сигналов SIGPIPE.

И все эти ERR_NET_COULD_NOT_GET_SOCKET_OPTION — плохая практика. Вы должны вернуть фактическое значение errno или, по крайней мере, вывести его не только в случае getsockopt(), но и во всех случаях ошибки.

И вы делаете connect() в режиме блокировки. Поэтому следующие select() совершенно бессмысленны.

01.05.2017
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..