Сбой клиентского приложения приводит к сбою сервера? (C ++) - PullRequest
0 голосов
/ 05 января 2011

Я не уверен, что это известная проблема, с которой я сталкиваюсь, но я не смог найти хорошую строку поиска, которая дала бы мне какие-либо полезные результаты. Во всяком случае, вот краткое изложение:

у нас есть относительно простое приложение, которое берет данные из источника (БД или файл) и передает эти данные по TCP на подключенные клиенты по мере поступления новых данных. Это относительно небольшое количество клиентов; я бы сказал, максимум 10 клиентов на сервер, поэтому у нас есть следующий грубый дизайн:

client: подключиться к серверу, установить на чтение (с тайм-аутом, установленным на более высокую, чем частота сообщений пульса сервера). Блокирует на чтение.

сервер: один поток прослушивания, который принимает подключения, а затем порождает поток записи для чтения из источника данных и записи на клиент. Писательский поток также отсоединяется (используя boost :: thread, поэтому просто вызовите функцию .detach ()). Он блокирует запись на неопределенный срок, но перед ошибкой проверяет ошибки. Мы запускаем серверы, используя один Perl-скрипт и вызывая «fork» для каждого серверного процесса.

Проблема (и): в случайные, казалось бы, моменты времени клиент завершает работу с «соединение прервано (SUCCESFUL)», указывающее, что удаленный сервер отключил сокет преднамеренно. Однако, когда это происходит, приложение SERVER также закрывается, без каких-либо ошибок или чего-либо еще. это просто падает

Теперь, чтобы решить эту проблему, мы имеем несколько экземпляров серверного приложения, запускаемого сценарием запуска, выполняющим разные файлы и разные порты. Когда ОДИН из серверов падает таким образом, ВСЕ серверы выходят из строя.

Сервер и клиент используют одну и ту же библиотеку «Соединение», созданную собственными силами. В основном это оболочка C ++ для вызовов сокетов C.

Вот примерный код для функции записи и чтения в библиотеке подключений:

int connectionTimeout_read = 60 * 60 * 1000; 
int Socket::readUntil(char* buf, int amount) const
    {
        int readyFds = epoll_wait(epfd,epEvents,1,connectionTimeout_read);
        if(readyFds < 0)
        {
            status = convertFlagToStatus(errno);
            return 0;
        }
        if(readyFds == 0)
        {
            status = CONNECTION_TIMEOUT;
            return 0;
        }
        int fd = epEvents[0].data.fd;
        if( fd != socket)
        {
            status = CONNECTION_INCORRECT_SOCKET;
            return 0;
        }
        int rec = recv(fd,buf,amount,MSG_WAITALL);

        if(rec == 0)
            status = CONNECTION_CLOSED;
        else if(rec < 0)
            status = convertFlagToStatus(errno);
        else
            status = CONNECTION_NORMAL;
        lastReadBytes = rec;
        return rec;

    }



int Socket::write(const void* buf, int size) const
    {

        int readyFds = epoll_wait(epfd,epEvents,1,-1);
        if(readyFds < 0)
        {
            status = convertFlagToStatus(errno);
            return 0;
        }
        if(readyFds == 0)
        {
            status = CONNECTION_TERMINATED;
            return 0;
        }
        int fd = epEvents[0].data.fd;
        if(fd != socket)
        {
            status = CONNECTION_INCORRECT_SOCKET;
            return 0;
        }
        if(epEvents[0].events != EPOLLOUT)
        {
            status = CONNECTION_CLOSED;
            return 0;
        }
        int bytesWrote = ::send(socket, buf, size,0);
        if(bytesWrote < 0)
            status = convertFlagToStatus(errno);
        lastWriteBytes = bytesWrote;
        return bytesWrote;

    }

Любая помощь в решении этой загадочной ошибки была бы великолепна! по крайней мере, я бы хотел, чтобы он НЕ приводил к сбою сервера, даже если происходит сбой клиента (что действительно странно для меня, поскольку двусторонняя связь отсутствует).

Также для справки приведен код прослушивания сервера:

while(server.getStatus() == connection::CONNECTION_NORMAL)
        {
            connection::Socket s = server.listen();
                if(s.getStatus() != connection::CONNECTION_NORMAL)
                {
                    fprintf(stdout,"failed to accept a socket. error: %s\n",connection::getStatusString(s.getStatus()));
                }

                DATASOURCE* dataSource;
                dataSource = open_datasource(XXXX);  /* edited */               if(dataSource == NULL)
                {
                    fprintf(stdout,"FATAL ERROR. DATASOURCE NOT FOUND\n");
                    return;
                }
                    boost::thread fileSender(Sender(s,dataSource));
                    fileSender.detach();


        }

... А также вот порожденная нить, отправляющая ребенка:

::signal(SIGPIPE,SIG_IGN);

    //const int headerNeeds = 29;
    const int BUFFERSIZE = 2000;
    char buf[BUFFERSIZE];

    bool running = true;
    while(running)
    {
             memset(buf,'\0',BUFFERSIZE*sizeof(char));
        unsigned int readBytes = 0;
        while((readBytes = read_datasource(buf,sizeof(unsigned char),BUFFERSIZE,dataSource)) == 0)
        {
            boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
        }
        socket.write(buf,readBytes);
        if(socket.getStatus() != connection::CONNECTION_NORMAL)
            running = false;

    }
    fprintf(stdout,"socket error: %s\n",connection::getStatusString(socket.getStatus()));
    socket.close();
    fprintf(stdout,"sender exiting...\n");

Любые идеи приветствуются! Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 05 января 2011

У вас , вероятно, все получилось задом наперед ... при сбое сервера ОС закроет все сокеты.Таким образом, сначала происходит сбой сервера, и клиент получает сообщение о разъединении (на самом деле флаг FIN в сегменте TCP), сбой не является результатом закрытия сокета.

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

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

Запись обработчика сигнала.Убедитесь, что он использует только необработанные функции ввода / вывода для регистрации проблем (открытие, запись, закрытие, не fwrite, не printf).

Проверьте возвращаемые значения.Проверьте наличие отрицательного возвращаемого значения из write в сокете, но проверьте все возвращаемые значения.

0 голосов
/ 07 января 2011

Спасибо за все комментарии и предложения. После просмотра кода и добавления обработки сигнала, как предложил Бен, сами приложения стали намного более стабильными. Спасибо за ваш вклад.

Первоначальная проблема, однако, была из-за мошеннического скрипта, который один из администраторов выполнял от имени пользователя root, который случайным образом убивал определенные процессы на компьютере на стороне сервера (я не буду вдаваться в то, что он пытался сделать в реальность; можно с уверенностью сказать, что это было глючит). Извлеченный урок: проверьте окружающую среду.

Спасибо всем за совет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...