2

I've started learning POCO C++ library and I'm stuck while trying to run 2 servers in the same application (so that they can use some common runtime variables). These are 2 different servers, one of them is TCP TimeServer and the other one is simple UDP EchoServer. The code:

#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerParams.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Timestamp.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/Exception.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include <iostream>


using Poco::Net::ServerSocket;
using Poco::Net::StreamSocket;
using Poco::Net::TCPServerConnection;
using Poco::Net::TCPServerConnectionFactory;
using Poco::Net::TCPServer;
using Poco::Timestamp;
using Poco::DateTimeFormatter;
using Poco::DateTimeFormat;
using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;


class TimeServerConnection : public TCPServerConnection
{
public:
    TimeServerConnection(const StreamSocket& s, const std::string& format) :
        TCPServerConnection(s),
        _format(format)
    {
    }

    void run()
    {
        Application& app = Application::instance();

        bool isOpen = true;
        Poco::Timespan timeOut(10, 0);
        unsigned char incommingBuffer[1000];

        app.logger().information("SYSLOG from " + this->socket().peerAddress().toString());

        while (isOpen) {
            if (socket().poll(timeOut, Poco::Net::Socket::SELECT_READ) == false) {
                std::cout << "TIMEOUT!" << std::endl << std::flush;
            } else {
                int nBytes = -1;
                try {
                    nBytes = socket().receiveBytes(incommingBuffer, sizeof(incommingBuffer));
                    std::cout << incommingBuffer << std::endl;
                } catch (Poco::Exception& exc) {
                    std::cerr << "Network error: " << exc.displayText() << std::endl;
                    isOpen = false;
                }

                if (nBytes == 0) {
                    std::cout << "Client closes connection!" << std::endl << std::flush;
                    isOpen = false;
                } else {
                    std::cout << "Receiving nBytes: " << nBytes << std::endl << std::flush;
                }
            }
        }

        try
        {
            Timestamp now;
            std::string dt(DateTimeFormatter::format(now, _format));
            dt.append("\r\n");
            socket().sendBytes(dt.data(), (int)dt.length());
        }
        catch (Poco::Exception& exc)
        { app.logger().log(exc); }
    }

private:
    std::string _format;
};


class TimeServerConnectionFactory : public TCPServerConnectionFactory
{
public:
    TimeServerConnectionFactory(const std::string& format) :
        _format(format)
    {
    }

    TCPServerConnection* createConnection(const StreamSocket& socket)
    { return new TimeServerConnection(socket, _format); }

private:
    std::string _format;
};


class UDPServer : public Poco::Util::ServerApplication
{
public:
    UDPServer(){}
    ~UDPServer(){}

protected:
    void initialize(Application& self)
    {
        loadConfiguration(); // load default configuration files, if present
        ServerApplication::initialize(self);
    }

    void uninitialize() { ServerApplication::uninitialize(); }

    int main(const std::vector<std::string>& args)
    {

        unsigned short port = (unsigned short)config().getInt("udpport", 9002);
        std::cout << "[UDP] Using port " << port << std::endl;
        std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));

        Poco::Net::SocketAddress socketaddress(Poco::Net::IPAddress(), 9001);
        Poco::Net::DatagramSocket datagramsocket(socketaddress);
        char buffer[1024]; // 1K byte

        while (1) {
            Poco::Net::SocketAddress sender;
            int n = datagramsocket.receiveFrom(buffer, sizeof(buffer) - 1, sender);
            buffer[n] = '\0';
            std::cout << sender.toString() << ":" << buffer << std::endl;
        }
        return 0;
    }
};

class TimeServer : public Poco::Util::ServerApplication
{
public:
    TimeServer() : _helpRequested(false)
    {
    }

    ~TimeServer()
    {
    }

protected:
    void initialize(Application& self)
    {
        loadConfiguration(); // load default configuration files, if present
        ServerApplication::initialize(self);
    }

    void uninitialize()
    {
        ServerApplication::uninitialize();
    }

    void defineOptions(OptionSet& options)
    {
        ServerApplication::defineOptions(options);

        options.addOption(
            Option("help", "h", "display help information on command line arguments")
            .required(false)
            .repeatable(false));
    }

    void handleOption(const std::string& name, const std::string& value)
    {
        ServerApplication::handleOption(name, value);

        if (name == "help")
            _helpRequested = true;
    }

    void displayHelp()
    {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.setHeader("A server application that serves the current date and time.");
        helpFormatter.format(std::cout);
    }

    int main(const std::vector<std::string>& args)
    {
        if (_helpRequested)
        {
            displayHelp();
        }
        else
        {
            unsigned short port = (unsigned short)config().getInt("tcpport", 9911);
            std::cout << "Using port " << port << std::endl;
            std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));

            ServerSocket svs(port);
            TCPServer srv(new TimeServerConnectionFactory(format), svs);
            srv.start();
            std::cout << "Server started!\n";
            waitForTerminationRequest();
            srv.stop();
            std::cout << "Server stopped!\n";
        }
        return Application::EXIT_OK;
    }

private:
    bool _helpRequested;
};


int main(int argc, char** argv)
{
    TimeServer app;
    UDPServer app2;
    app.run(argc, argv);
    app2.run(argc, argv);
}

In the end of code I have int main() method where I'm trying to run 2 servers. However I get assertion violation here. There is a similar question on StackOverflow, however boost is used there while I'm using plain C++, so that solution is not relevant to me.

How can I run simultaneously these 2 servers?

Groosha
  • 2,897
  • 2
  • 22
  • 38

2 Answers2

1

ServerApplication was not designed for multiple instances. What you should do is run one ServerApplication and launch TCPServer and UDPServer in that application.

Alex
  • 5,159
  • 4
  • 25
  • 33
  • Am I correct, that one `ServerApplication` can only use one port at a time? – Groosha Dec 11 '17 at 07:55
  • ServerApplication does not have any direct correlation or dependency to network ports. I'm also not sure what exactly do you mean by "use one port at a time"? – Alex Dec 11 '17 at 16:46
  • Is it possible to launch one `ServerApplication` which will handle 2 different types of requests? For example, HTTP Server on port 80 and UDP server on port 9999 within one `ServerApplication` (so they share some data)? – Groosha Dec 12 '17 at 07:40
0

Actually if you want to made like this (as you question), seperated both tcp (a) class and udp (b) class.

Call both in other class (c) and define which one (c) -> (a) (c) -> (b) u need to call first and when. So u need make condition and decision.

Note: give them space time before run to made poco breath.

Spider Lynxz
  • 1
  • 1
  • 2