Multiple Http Servers with Poco and Boost C++

2020-06-27 01:38发布

问题:

I'm trying to create multiple Http servers with Poco::Net and Boost libraries, but is occurring the following error internally in Poco file Application.cpp:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Assertion violation: _pInstance == 0 [in file "src/Application.cpp", line 115]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

I'm using the code below:

#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <boost/asio/io_service.hpp>

boost::asio::io_service service(100);

class RequestHandler : public Poco::Net::HTTPRequestHandler {
public:
    RequestHandler(){}
    void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){}
};

class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
    RequestHandlerFactory(){}
    Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request)
    {
        return new RequestHandler();
    }
};

class HttpServer :
        public Poco::Util::ServerApplication,
        public boost::enable_shared_from_this<HttpServer>{

    Poco::Net::ServerSocket svs;
    Poco::Net::HTTPServer srv;

public:
    HttpServer(std::string address_, Poco::UInt16 port_):
        svs(Poco::Net::SocketAddress(address_.empty() ? "127.0.0.1" : address_, port_)),
        srv(new RequestHandlerFactory(), svs, new Poco::Net::HTTPServerParams)
    {
        svs.setReuseAddress(true);
        svs.setReusePort(true);
    }

    virtual ~HttpServer(){}

    void start()
    {
        service.post(
            boost::bind(&HttpServer::exec, shared_from_this()));
    }

    void stop()
    {
        srv.stop();
    }

private:
    void exec()
    {
        srv.start();
        waitForTerminationRequest();
        srv.stop();
    }    
};

This is the main code of server, and Im creating the servers at main function for example. The service.post call, is for asynchronous call of method exec and the construction of service(100) refers to a thread pool of size 100.

The server is created as follows:

boost::shared_ptr<HttpServer> server(
        new HttpServer("", 8080));

boost::shared_ptr<HttpServer> server2(
        new HttpServer("", 8181));

server->start();
server2->start(); // Error occurs here

The error is shown when the second server is started.

回答1:

HttpServerApplication is not designed to be used in this way. It's designed to be a singleton.

http://pocoproject.org/docs/Poco.Util.ServerApplication.html

You can fix that part by not deriving from ServerApplication in your HttpServer class. After all, you want multiple servers, not applications.

In my simplest test, the following works:

#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Net/HTTPServer.h>
#include <Poco/Util/ServerApplication.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>

boost::asio::io_service service(100);

class RequestHandler : public Poco::Net::HTTPRequestHandler {
public:
    RequestHandler(){}
    void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response){}
};

class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
    RequestHandlerFactory(){}
    Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request)
    {
        return new RequestHandler();
    }
};

class HttpServer : public boost::enable_shared_from_this<HttpServer>{

    Poco::Net::ServerSocket svs;
    Poco::Net::HTTPServer srv;

public:
    HttpServer(std::string address_, Poco::UInt16 port_):
        svs(Poco::Net::SocketAddress(address_.empty() ? "127.0.0.1" : address_, port_)),
        srv(new RequestHandlerFactory(), svs, new Poco::Net::HTTPServerParams)
    {
        svs.setReuseAddress(true);
        svs.setReusePort(true);
    }

    virtual ~HttpServer(){}

    void start()
    {
        service.post(
            boost::bind(&HttpServer::exec, shared_from_this()));
    }

    void stop()
    {
        srv.stop();
    }

private:
    void exec()
    {
        srv.start();
    }    
};

In main, I create a subclass of ServerApplication for the sole reason of claling the protected method waitForTerminationRequest() before exit of main. If you need argument parsing, of course pass argc, argv to app.run. But at this point I don't see the need.

int main() {
    struct App : Poco::Util::ServerApplication {
        ~App() {
            waitForTerminationRequest();
        }
    } app;

    for (int i=0; i<2; ++i) {
        boost::make_shared<HttpServer>("", 8080+i)->start();
    }
}

OLD ANSWER TEXT

How are you instantiating the HttpServer objects?

  1. You should, obviously, use boost::make_shared<HttpServer>(host, port) (or shared_ptr<HttpServer>(new HttpServer(host, port))) for enable_shared_from_this to be allowed. Don't forget to hold on to the shared ptr at least until the service has been started (by posting it's exec() call to io_service).

    Specifically, this is illegal:

    for (int i=0; i<10; ++i) {
        HttpServer s("", 8080+i);
        s.start();
    }
    

    Instead, use e.g.

    for (int i=0; i<10; ++i) {
        boost::make_shared<HttpServer>("", 8080+i)->start();
    }
    
  2. Also, if you're going to run it on a pool of 100 threads, you need to either use a strand (for a logical thread of execution) or lock the HttpServer object before you use it on any thread.

    • Why do I need strand per connection when using boost::asio?