Отправленные сервером события с использованием Poco::Net::HTTPRequestHandler

Я пытаюсь «передавать» данные на страницу HTML5, используя события, отправленные сервером.

Это руководство http://www.html5rocks.com/en/tutorials/eventsource/basics/ был очень полезен для работы на стороне клиента.

Но на стороне сервера я делаю что-то похожее на пример HTTPServer в http://pocoproject.org/slides/200-Network.pdf

Учебник html5rocks.com дал мне следующую идею для кода обработчика запросов:

void MyRequestHandler::handleRequest (HTTPServerRequest &req, HTTPServerResponse &resp)
{
    resp.setStatus(HTTPResponse::HTTP_OK);

    resp.add("Content-Type", "text/event-stream");
    resp.add("Cache-Control", "no-cache");

    ostream& out = resp.send();

    while (out.good())
    {
        out << "data: " << "some data" << "\n\n";
        out.flush();

        Poco::Thread::sleep(500)
    }
}

и источник страницы HTML5:

<!DOCTYPE html>
<html>
    <head>
            <title>HTLM5Application</title>
    </head>
    <body>
        <p id="demo">hello</p>
        <script>
            var msgCounter = 0;
            var source;
            var data;
            if(typeof(EventSource) !== "undefined")
            {
                source = new EventSource('/stream');
                document.getElementById("demo").innerHTML = "Event source created";
            }
            else
            {
                document.getElementById("demo").innerHTML = "Are you using IE ?";
            }

            source.addEventListener('message', function(e)
            {
                msgCounter++;
                document.getElementById("demo").innerHTML = "Message received (" + msgCounter + ") !<br/>"+ e.data;
            }, false);
        </script>
    </body>
</html>

Хорошо то, что при открытии html-страницы данные передаются в потоковом режиме, и я получаю правильный вывод (текст между тегами обновляется, как и ожидалось.

Проблема в том, что когда я закрываю страницу в браузере, программа POCO вылетает, а в консоли получаю следующее сообщение:

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Process returned 3 (0x3)   execution time : 22.234 s
Press any key to continue.

(Я использую Code::Blocks, поэтому отображается возвращаемое значение и время выполнения)

Событие, когда я помещаю цикл while() между try{ }catch(...){}, программа все равно падает без входа в catch (то же самое происходит, когда я помещаю все содержимое main() между try/catch )

Основная программа содержит только эти инструкции:

int main(int argc, char* argv[])
{
    MyServerApp myServer;
    myServer.run(argc, argv);

    return 0;
}

Я хочу знать, что могло вызвать этот сбой и как я могу это исправить, пожалуйста.

Заранее спасибо за вашу помощь :)


person maddouri    schedule 03.04.2013    source источник
comment
Привет, ты нашел решение этой проблемы? Я нахожусь в такой же ситуации и рассматриваю возможность использования реактора в сокете сервера для закрытия соединения вручную.   -  person Maher4Ever    schedule 18.07.2015
comment
В то время я разрабатывал на ОС Windows7. Если я правильно помню, поэтому попытка/поймать не сработала. Таким образом, на этой платформе не использовались события, отправленные сервером. Однако я думаю, что тот же код (т.е. с обработкой исключений) должен работать в Linux.   -  person maddouri    schedule 19.07.2015
comment
Спасибо за ответ. Я также сейчас разрабатываю сервер на Windows 7. Покопавшись в документации, я смог справиться с исключением, зарегистрировав собственный обработчик ошибок, который игнорирует обработчик, возникающий при отключении клиента.   -  person Maher4Ever    schedule 20.07.2015
comment
Прохладный. Не могли бы вы поделиться своим кодом? Я думаю, что это было бы полезно для других людей, которые имеют дело с аналогичными проблемами.   -  person maddouri    schedule 20.07.2015
comment
Абсолютно! Я опубликую ответ со своим кодом, когда вернусь домой.   -  person Maher4Ever    schedule 20.07.2015


Ответы (2)


Для всех, кто заинтересован, я смог решить эту проблему, зарегистрировав свой собственный обработчик ошибок, который просто игнорирует исключение, возникающее при отключении SSE-клиента:

#include <Poco\ErrorHandler.h>

// Other includes, using namespace..., etc.

class ServerErrorHandler : public ErrorHandler
{
public:
    void exception(const Exception& e)
    {
        // Ignore an exception that's thrown when an SSE connection is closed. 
        //
        // Info: When the server is handling an SSE request, it keeps a persistent connection through a forever loop.
        //       In order to handle when a client disconnects, the request handler must detect such an event. Alas, this
        //       is not possible with the current request handler in Poco (we only have 2 params: request and response).
        //       The only hack for now is to simply ignore the exception generated when the client disconnects :(
        //
        if (string(e.className()).find("ConnectionAbortedException") == string::npos)
            poco_debugger_msg(e.what());
    }
};

class ServerApp : public ServerApplication 
{
protected:
    int main(const vector<string>& args) 
    {
        // Create and register our error handler
        ServerErrorHandler error_handler;
        ErrorHandler::set(&error_handler);

        // Normal server code, for example:
        HTTPServer server(new RequestHandlerFactory, 80, new HTTPServerParams);
        server.start();

        waitForTerminationRequest();
        server.stop();

        return Application::EXIT_OK;
    }
};


POCO_SERVER_MAIN(ServerApp);

Тем не менее, я должен сказать, что это уродливый хак. Более того, обработчик ошибок является глобальным для приложения, что делает его еще менее желательным решением. Правильным способом было бы обнаружить отключение и обработать его. Для этого Poco должен передать SocketStream обработчику запросов.

person Maher4Ever    schedule 20.07.2015

Вы можете изменить свой код, чтобы перехватывать исключения Poco:

try {
    MyServerApp myServer;
    return myServer.run(argc, argv);        
}catch(const Poco::Exception& ex) {
    std::cout << ex.displayText() << std::endl;
    return Poco::Util::Application::EXIT_SOFTWARE;
}
person Giovani    schedule 24.01.2014