WINDOWS下POCO使用总结

我花了很多时间去组装一个完整的服务器程序,其中包括了命令行解析,日志系统,TCP,UDP,HTTP(HTTPS),WEBSOCKET,DATABASE等子模块,INI,JSON,XML等文件格式的解析。其实,自己摸索一遍之后总结一个框架出来也是不错的,但是,自己写太费事,而用开源组装,面对浩如烟海,各种风格理念不一,半生不死的开源库,又实在难爱的起来。翻来覆去,还是POCO好。

1.POCO安装

自从有了vcpkg,安装第三方库不知道有多方便了(第二步编译出vcpkg 程序)
(1)git clone https://github.com/Microsoft/vcpk
(2)bootstrap-vcpkg.bat
(3)vcpkg install poco:x64-windows

2.POCO网站

https://pocoproject.org/

3.命令行解析,日志系统初始化,Application子系统启用

main.cpp

// Matchmaker.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "cirrusserver.h"
#include "matchmaker.h"

class App : public Poco::Util::ServerApplication
{
public:
    App() { }

    const char* name() const override
    {
        return "Matchmaker";
    }

    void initialize(Application& self) override
    {
        //Create Log Directory
        Poco::File logFile("log");
        if (!logFile.exists())
        {
            logFile.createDirectory();
        }

        //Log Channel
        Poco::AutoPtr splitterChannel(new Poco::SplitterChannel);
        Poco::AutoPtr consoleChannel(new Poco::ConsoleChannel);
        splitterChannel->addChannel(consoleChannel);

        Poco::AutoPtr fileChannel(new Poco::FileChannel);
        fileChannel->setProperty("path", "log/Matchmaker.log");
        fileChannel->setProperty("archive", "timestamp");
        splitterChannel->addChannel(fileChannel);

        Poco::AutoPtr patternFormatter(new Poco::PatternFormatter);
        patternFormatter->setProperty("pattern", "%Y-%m-%d %H:%M:%S %s: %t");
        Poco::AutoPtr formattingChannel(new Poco::FormattingChannel(patternFormatter, splitterChannel));

        Poco::Logger::root().setChannel(formattingChannel);

        //init
        Poco::Util::ServerApplication::initialize(self);
    }

    void uninitialize() override
    {
        Poco::Util::ServerApplication::uninitialize();
    }

    void reinitialize(Application& app) override
    {
        Poco::Util::ServerApplication::reinitialize(app);
    }

    void defineOptions(Poco::Util::OptionSet& options) override
    {
        Poco::Util::ServerApplication::defineOptions(options);

        setUnixOptions(true);

        options.addOption(
            Poco::Util::Option("help", "h", "display help information on command line arguments", false)
            .repeatable(false)
            .callback(Poco::Util::OptionCallback (this, &App::handleOptionHelp)));

        options.addOption(
            Poco::Util::Option("config", "c", "config file path", false)
            .repeatable(false)
            .argument("path", true)
            .callback(Poco::Util::OptionCallback(this, &App::handleOptionConfig)));
    }

    void handleOptionHelp(const std::string& name, const std::string& value)
    {
        Poco::Util::HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.format(std::cerr);
        stopOptionsProcessing();

        isExit = true;
    }

    void handleOptionConfig(const std::string& name, const std::string& value)
    {
        Poco::File iniFile(value);
        if (iniFile.exists())
        {
            loadConfiguration(value);
        }
        else
        {
            std::cerr << "File Not Found:" << value;
            isExit = true;
        }
    }

    int main(const std::vector& args) override
    {
        if (!isExit)
        {
            //Server
            CirrusServer::instance().Init(config());
            CirrusServer::instance().Start();

            MatchMaker matchMaker;
            matchMaker.Init(config());
            matchMaker.Start();

            waitForTerminationRequest();

            matchMaker.Stop();
            CirrusServer::instance().Stop();
        }
        return Poco::Util::ServerApplication::EXIT_OK;
    }

private:
    bool isExit = false;
};

POCO_SERVER_MAIN(App);

4.HTTP

matchmaker.h

#ifndef MATCHMAKER_H
#define MATCHMAKER_H
#include 
#include 
#include "cirrusserver.h"
#include 
#include 
#include 

class MatchMaker 
{
public:
    explicit MatchMaker();

    bool Init(const Poco::Util::LayeredConfiguration& config);
    bool Start();
    void Stop();

public:
    Poco::SharedPtr _server;

    std::string _address;
    int _port;
};

#endif // MATCHMAKER_H

matchmaker.cpp

#include "matchmaker.h"
#include "cirrusserver.h"
#include 
#include 
#include 
#include 
#include 
#include 

class RootRequestHandler : public Poco::Net::HTTPRequestHandler
{
public:
    void handleRequest(Poco::Net::HTTPServerRequest& request,
        Poco::Net::HTTPServerResponse& response)
    {
        CirrusClient cirrusClient;
        if (CirrusServer::instance().GetAvailableCirrusServer(cirrusClient))
        {
            std::string url = Poco::format("http://%s:%d/", cirrusClient.address, cirrusClient.port);
            response.redirect(url.c_str());

            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.information("Redirect to " + url);
        }
        else
        {
            response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
            response.setContentType("text/plain");
            std::ostream& out = response.send();
            out << "No Cirrus servers are available";
        }
    }
};

class CustomRequestHandler : public Poco::Net::HTTPRequestHandler
{
public:
    void handleRequest(Poco::Net::HTTPServerRequest& request,
        Poco::Net::HTTPServerResponse& response)
    {
        CirrusClient cirrusClient;
        if (CirrusServer::instance().GetAvailableCirrusServer(cirrusClient))
        {
            std::string url = Poco::format("http://%s:%d/%s", cirrusClient.address, cirrusClient.port, request.getURI());
            response.redirect(url.c_str());

            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.information("Redirect to " + url);
        }
        else
        {
            response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
            response.setContentType("text/plain");
            std::ostream& out = response.send();
            out << "No Cirrus servers are available";
        }
    }
};

class NotFoundRequestHandler : public Poco::Net::HTTPRequestHandler
{
public:
    void handleRequest(Poco::Net::HTTPServerRequest& request,
        Poco::Net::HTTPServerResponse& response)
    {
        response.setStatus(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
        response.setContentType("text/plain");
        std::ostream& out = response.send();
        out << "404 Not Found";
    }
};

class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
    RequestHandlerFactory() {};
    ~RequestHandlerFactory() {};
public:
    virtual Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request)
    {
        if (request.getURI() == "/")
        {
            return new RootRequestHandler();
        }
        else if(Poco::RegularExpression("(/custom_html/(.*))").match(request.getURI()))
        {
            return new CustomRequestHandler();
        }
        else
        {
            return new NotFoundRequestHandler();
        }
    };
};

MatchMaker::MatchMaker():_port(9999)
{

}

bool MatchMaker::Init(const Poco::Util::LayeredConfiguration& config)
{
    try
    {
        _address = config.getString("http.address", "0.0.0.0");
        _port = config.getInt("http.port", 9999);

        Poco::AutoPtr params(new Poco::Net::HTTPServerParams);
        params->setMaxQueued(64);
        params->setMaxThreads(16);

        _server = Poco::SharedPtr(new Poco::Net::HTTPServer(new RequestHandlerFactory(), Poco::Net::ServerSocket(_port), params));

        Poco::Logger& logger = Poco::Logger::get("Matchmaker");
        logger.information(Poco::format("Init Http(*:%d) Succeed", _port));

        return true;
    }
    catch(Poco::Exception* ex)
    {
        Poco::Logger& logger = Poco::Logger::get("Matchmaker");
        logger.error(ex->message());
    }
    
    return false;
}

bool MatchMaker::Start()
{
    if (_server)
    {
        try
        {
            _server->start();

            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.information(Poco::format("Start Http(*:%d) Succeed", _port));

            return true;
        }
        catch (Poco::Exception * ex)
        {
            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.error(ex->message());
        }
    }

    return false;
}

void MatchMaker::Stop()
{
    if (_server)
    {
        _server->stop();
    }
}

5.TCP

cirrusserver.h

#ifndef TCP_SERVER_H_
#define TCP_SERVER_H_
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ServerConnection;
struct CirrusClient
{
    std::string address;
    int port;
    int numConnectedClients;
};

class CirrusServer
{
public:
    CirrusServer();
    ~CirrusServer();

    static CirrusServer& instance()
    {
        static Poco::SingletonHolder sh;
        return *sh.get();
    }

    bool Init(const Poco::Util::LayeredConfiguration& config);
    bool Start();
    void Stop();
    
    bool GetAvailableCirrusServer(CirrusClient& cirrusClient)
    {
        std::lock_guard lock(_mutex);
        if (_users.empty())
        {
            return false;
        }

        cirrusClient = _users.front();
        _users.pop_front();

        return true;
    }

    void AddCirrusServer(const CirrusClient& cirrusClient)
    {
        std::lock_guard  lock(_mutex);
        _users.push_back(cirrusClient);
    }

    void RemoveCirrusServer(const CirrusClient& cirrusClient)
    {
        std::lock_guard  lock(_mutex);
        for(auto it = _users.begin(); it != _users.end(); it++)
        {
            if ((it->address == cirrusClient.address) && (it->port == cirrusClient.port))
            {
                _users.erase(it);
                return;
            }
        }
    }

    void IncrementCirrusServer(const CirrusClient& cirrusClient)
    {
        std::lock_guard  lock(_mutex);
        for (auto it = _users.begin(); it != _users.end(); it++)
        {
            if ((it->address == cirrusClient.address) && (it->port == cirrusClient.port))
            {
                it->numConnectedClients++;
                return;
            }
        }
    }

    void DecrementCirrusServer(const CirrusClient& cirrusClient)
    {
        std::lock_guard  lock(_mutex);
        for (auto it = _users.begin(); it != _users.end(); it++)
        {
            if ((it->address == cirrusClient.address) && (it->port == cirrusClient.port))
            {
                it->numConnectedClients--;
                return;
            }
        }
    }
    
private:
    Poco::SharedPtr _server;

    std::list _users;
    std::string _address;
    int _port;

    std::mutex _mutex;
    std::thread _thread;
};

#endif

cirrusserver.cpp

#include "cirrusserver.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ServerConnection : public Poco::Net::TCPServerConnection
{
public:
    ServerConnection(const Poco::Net::StreamSocket& streamSocket) :TCPServerConnection(streamSocket)
    {

    }

    void run()
    {
        Poco::Logger& logger = Poco::Logger::get("Matchmaker");
        logger.information("New connection from: " + socket().peerAddress().host().toString());

        bool isOpen = true;
        Poco::Timespan timeOut(10, 0);
        Poco::Array incommingBuffer;
        while (isOpen) 
        {
            if (socket().poll(timeOut, Poco::Net::Socket::SELECT_READ))
            {
                int nBytes = -1;
                try 
                {
                    nBytes = socket().receiveBytes(incommingBuffer.data(), incommingBuffer.size());
                }
                catch (Poco::Exception & exc) 
                {
                    isOpen = false;
                    CirrusServer::instance().RemoveCirrusServer(_cirrusClient);
                    logger.error("Network Error: " + exc.displayText());
                }

                if (nBytes == 0) 
                {
                    isOpen = false;
                    CirrusServer::instance().RemoveCirrusServer(_cirrusClient);
                    logger.warning("Client Closes Connection!");
                }
                else 
                {
                    Handle(std::string(incommingBuffer.data(), nBytes));
                }
            }
        }

        logger.information("Connection finished!");
    }

    void Handle(const std::string& command)
    {
        Poco::JSON::Parser parse;
        
        Poco::JSON::Object j = *parse.parse(command).extract();
        std::string type = j.get("type").toString();
        if ("connect" == type)
        {
            _cirrusClient.address = j.get("address").toString();
            _cirrusClient.port = j.get("port").convert();
            CirrusServer::instance().AddCirrusServer(_cirrusClient);
        }
        else if ("clientConnected" == type)
        {
            CirrusServer::instance().IncrementCirrusServer(_cirrusClient);
        }
        else if ("clientDisconnected" == type)
        {
            CirrusServer::instance().DecrementCirrusServer(_cirrusClient);
        }
        else
        {
            CirrusServer::instance().RemoveCirrusServer(_cirrusClient);
        }
        
    }
private:
    CirrusClient _cirrusClient;
};

CirrusServer::CirrusServer() :_port(9999)
{

}

CirrusServer::~CirrusServer()
{

}


bool CirrusServer::Init(const Poco::Util::LayeredConfiguration& config)
{
    try
    {
        _address = config.getString("tcp.address", "127.0.0.1");
        _port = config.getInt("tcp.port", 90);

        Poco::Net::TCPServerParams::Ptr param = new Poco::Net::TCPServerParams;
        param->setMaxQueued(64);
        param->setMaxThreads(16);

        _server = Poco::SharedPtr(new Poco::Net::TCPServer(new Poco::Net::TCPServerConnectionFactoryImpl(), Poco::Net::ServerSocket(_port)));

        Poco::Logger& logger = Poco::Logger::get("Matchmaker");
        logger.information(Poco::format("Init Tcp(*:%d) Succeed", _port));

        return true;
    }
    catch (Poco::Exception * ex)
    {
        Poco::Logger& logger = Poco::Logger::get("Matchmaker");
        logger.error(ex->message());
    }

    return false;
}

bool CirrusServer::Start()
{
    if (_server)
    {
        try {
            _server->start();
            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.information(Poco::format("Start Tcp(*:%d) Succeed", _port));

            return true;
        }
        catch (Poco::Exception * ex)
        {
            Poco::Logger& logger = Poco::Logger::get("Matchmaker");
            logger.error(ex->message());
        }
    }

    return false;
}

void CirrusServer::Stop()
{
    if (_server)
    {
        _server->stop();

        std::lock_guard  lock(_mutex);
        _users.clear();
    }
}

你可能感兴趣的:(WINDOWS下POCO使用总结)