美文网首页
WINDOWS下POCO使用总结

WINDOWS下POCO使用总结

作者: qlaiaqu | 来源:发表于2019-09-27 15:37 被阅读0次

    我花了很多时间去组装一个完整的服务器程序,其中包括了命令行解析,日志系统,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 <Poco/Util/Option.h>
    #include <Poco/Util/OptionSet.h>
    #include <Poco/File.h>
    #include <Poco/Logger.h>
    #include <Poco/ConsoleChannel.h>
    #include <Poco/FileChannel.h>
    #include <Poco/SplitterChannel.h>
    #include <Poco/FormattingChannel.h>
    #include <Poco/PatternFormatter.h>
    #include <Poco/AutoPtr.h>
    #include <Poco/Util/HelpFormatter.h>
    #include <Poco/Util/IniFileConfiguration.h>
    #include <Poco/Util/ServerApplication.h>
    #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<Poco::SplitterChannel> splitterChannel(new Poco::SplitterChannel);
            Poco::AutoPtr<Poco::ConsoleChannel> consoleChannel(new Poco::ConsoleChannel);
            splitterChannel->addChannel(consoleChannel);
    
            Poco::AutoPtr<Poco::FileChannel> fileChannel(new Poco::FileChannel);
            fileChannel->setProperty("path", "log/Matchmaker.log");
            fileChannel->setProperty("archive", "timestamp");
            splitterChannel->addChannel(fileChannel);
    
            Poco::AutoPtr<Poco::PatternFormatter> patternFormatter(new Poco::PatternFormatter);
            patternFormatter->setProperty("pattern", "%Y-%m-%d %H:%M:%S %s: %t");
            Poco::AutoPtr<Poco::FormattingChannel> 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 <App>(this, &App::handleOptionHelp)));
    
            options.addOption(
                Poco::Util::Option("config", "c", "config file path", false)
                .repeatable(false)
                .argument("path", true)
                .callback(Poco::Util::OptionCallback<App>(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<std::string>& 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 <string>
    #include <memory>
    #include "cirrusserver.h"
    #include <Poco/SharedPtr.h>
    #include <Poco/Net/HTTPServer.h>
    #include <Poco/Util/Application.h>
    
    class MatchMaker 
    {
    public:
        explicit MatchMaker();
    
        bool Init(const Poco::Util::LayeredConfiguration& config);
        bool Start();
        void Stop();
    
    public:
        Poco::SharedPtr<Poco::Net::HTTPServer> _server;
    
        std::string _address;
        int _port;
    };
    
    #endif // MATCHMAKER_H
    

    matchmaker.cpp

    #include "matchmaker.h"
    #include "cirrusserver.h"
    #include <Poco/Format.h>
    #include <Poco/Exception.h>
    #include <Poco/RegularExpression.h>
    #include <Poco/Net/HTTPRequestHandler.h>
    #include <Poco/Net/HTTPServerRequest.h>
    #include <Poco/Net/HTTPServerResponse.h>
    
    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<Poco::Net::HTTPServerParams> params(new Poco::Net::HTTPServerParams);
            params->setMaxQueued(64);
            params->setMaxThreads(16);
    
            _server = Poco::SharedPtr<Poco::Net::HTTPServer >(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 <list>
    #include <mutex>
    #include <Poco/AutoPtr.h>
    #include <Poco/SharedPtr.h>
    #include <Poco/Net/TCPServer.h>
    #include <Poco/SingletonHolder.h>
    #include <Poco/Util/LayeredConfiguration.h>
    
    class ServerConnection;
    struct CirrusClient
    {
        std::string address;
        int port;
        int numConnectedClients;
    };
    
    class CirrusServer
    {
    public:
        CirrusServer();
        ~CirrusServer();
    
        static CirrusServer& instance()
        {
            static Poco::SingletonHolder<CirrusServer> 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<Poco::Net::TCPServer> _server;
    
        std::list<CirrusClient> _users;
        std::string _address;
        int _port;
    
        std::mutex _mutex;
        std::thread _thread;
    };
    
    #endif
    

    cirrusserver.cpp

    #include "cirrusserver.h"
    #include <array>
    #include <Poco/Array.h>
    #include <Poco/Logger.h>
    #include <Poco/Net/TCPServerConnection.h>
    #include <Poco/Net/TCPServerConnectionFactory.h>
    #include <Poco/Net/StreamSocket.h>
    #include <Poco/Dynamic/Var.h>
    #include <Poco/Dynamic/Pair.h>
    #include <Poco/Dynamic/VarIterator.h>
    #include <Poco/JSON/Parser.h>
    #include <Poco/JSON/Object.h>
    
    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<char, 8192> 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<Poco::JSON::Object::Ptr>();
            std::string type = j.get("type").toString();
            if ("connect" == type)
            {
                _cirrusClient.address = j.get("address").toString();
                _cirrusClient.port = j.get("port").convert<int>();
                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<Poco::Net::TCPServer >(new Poco::Net::TCPServer(new Poco::Net::TCPServerConnectionFactoryImpl<ServerConnection>(), 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使用总结

          本文链接:https://www.haomeiwen.com/subject/mscauctx.html