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 <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();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容