0

I need to set up an application that listens on a number of Urls but do not know when I start how many - this will eventually be read from a database but for the moment they are hard coded in a demo.

// MultipleListenerTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <string>
#include <vector>

#include <cpprest\http_listener.h>
#include <cpprest\http_client.h>

#include "TestListener.h"               // listener object

using namespace web::http::client;
using namespace web::http::experimental::listener;
using namespace web::http;
using namespace web;

void SetListenerArray();

typedef std::vector<http_listener> httpListeners;

listenerCollection listeners;

TestListener listener1;
TestListener Listener2;
TestListener Listener3;

httpListeners httpListenersList;
void handle_get(http_request request);
void handle_post(http_request request);
void handle_put(http_request request);
void handle_del(http_request request);


int _tmain(int argc, _TCHAR* argv[])
{

    SetListenerArray();

    for each (TestListener tl  in listeners)
    {
        http_listener l(tl.GetUrl());

        l.support(methods::GET, handle_get);
        l.support(methods::POST, handle_post);
        l.support(methods::PUT, handle_put);
        l.support(methods::DEL, handle_del);

        l.open().wait();
        httpListenersList.push_back(l);
    }

    std::string line;
    std::cout << "Press enter to exit" << std::endl;
    std::getline(std::cin, line);

    //listenerVoices_1.close().wait();
    //listenerVoices_2.close().wait();

    return 0;
}

void SetListenerArray()
{

    // first listener
    listener1.SetListenerName(to_string_t("FirstListener"));
    listener1.SetUrl(to_string_t("http://localhost:8010"));
    listeners.push_back(listener1);

    // second listener
    Listener2.SetListenerName(to_string_t("Second Listener"));
    Listener2.SetUrl(to_string_t("http://localhost:8020"));
    listeners.push_back(Listener2);

    //third listener
    Listener3.SetListenerName(to_string_t("Third Listener"));
    Listener3.SetUrl(to_string_t("http://localhost:8030"));
    listeners.push_back(Listener3);

}

void handle_get(http_request request)
{
    for each (TestListener tl  in listeners)
    {
        std::cout << to_utf8string(tl.GetListenerName());
    }
}

void handle_post(http_request request)
{

}

void handle_put(http_request request)
{

}

void handle_del(http_request request)
{

}

With the above code the line

httpListenersList.push_back(l);

fails to compile with the error

Error 1 error C2248: 'web::http::experimental::listener::http_listener::http_listener' : cannot access private member declared in class 'web::http::experimental::listener::http_listener' c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0 593 1 MultipleListenerTest

If I leave it out then none of the listeners are actually listening and you get This webpage is not available in Chrome at least.

So how can you set up multiple listeners when you do not know before hand how many are required?

Paul S Chapman
  • 832
  • 10
  • 37

1 Answers1

2

Use threading - each thread executes one of the listeners.

The following is the method in a class which executes a listener.

void Listener::Start()
{
    Logger logger;
    std::string logMessage("Starting '" + to_utf8string(GetName()) + "' Listener");
    http_listener httpListener(GetUri());
    std::string listenerName(to_utf8string(name));

    logger.log(logMessage);

    // listener recieves a GET request.
    httpListener.support(methods::GET, [listenerName](http_request request)
    {
        Logger logger;

        std::string logMessage("GET request recieved from " + listenerName);

        logger.log(logMessage);

        // dummy line just till routing is dealt with
        request.reply(status_codes::OK, logMessage);

    });

    // open listener and route request 
    httpListener
        .open()
        .then([&httpListener,listenerName](){ 
            Logger logger;
            std::string logMessage(listenerName + " started");

            logger.log(logMessage);

        }).wait();

    // JUST WAIT - we do not want the application to stop
    while (true);

}

Then in the program execute the following code which collects all of the listeners in a vector.

Then scans the vectors to create each thread.

RouteMaps rm;
Logger logger;
Listeners ls;
std::vector<std::thread> listener_threads;
std::vector<Listener> lvector;

std::cout.sync_with_stdio(true);

// collect all the listeners into a vector

for (auto& m : rm.Select()) {
    for (auto& l : ls.Select(m.GetId())) {
        std::string logMessage("Retrieved Listener " + to_utf8string(l.GetName()));
        logger.log(logMessage);
        lvector.push_back(l);
    }
}

std::cout << "Starting listeners" << std::endl;
// now create threads for each listener
for (auto& lstnr : lvector)
{
    listener_threads.push_back(std::thread{ &Listener::Start, &lstnr });
}

The application will however drop through and end - stopping each thread. Now at the bottom of each thread there is a permanent loop - that means no thread will actually end.

To stop the program execution killing each thread they need to be joined to the main thread with the following loop.

for (auto& t : listener_threads)
{
    t.join();
}

In this way we now have a number of threads and you do not have to define in code how many.

Now when I place the domain of each listener in a browser we get a reply from each one.

Paul S Chapman
  • 832
  • 10
  • 37