手把手教你使用Poco框架开发服务器应用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近接触一个服务器项目,想来想去还是用C++来开发,C++应该来说没有像Spring那么顶顶大名的的框架,但是好的框架还是有的,要不然纯手写服务器要开发到什么时候。今天选的Poco就是相当不错的一个框架,不说多丰富吧,至少开发服务器用到的东西都有了。

接下来从0开始教你使用Poco开发服务器应用

OS:Ubuntu Server x64


一、Poco是什么?

Poco(POrtable COmponents)是一个用于构建网络和互联网应用程序的开源 C++ 类库集合。它提供了丰富的功能,包括网络通信、文件系统操作、进程管理、数据库访问等。Poco 的设计目标是提供一个跨平台的、易于使用的 C++ 库,以帮助开发人员构建高效、可靠的应用程序。

二、准备工作

1.下载源码

git clone https://github.com/pocoproject/poco.git

2.下载压缩包

Poco官网

3.使用apt安装

有网络的建议使用下面的方式,编译的话要配环境,出了问题很多人解决不了。

sudo apt update
sudo apt install libpoco-dev

三、开始编译

使用apt安装的跳过这一步,前两种下载源代码的都要经历编译这一步.

cd poco
mkdir cmake-build
cd cmake-build
cmake ..
make -j$(nproc)

等待编译完成,安装(默认/usr/local):

sudo make install

四、开始使用

这里演示下MySQL数据库的使用,其他的数据库请看官方文档。

确保你已经有MySQL服务器的账号密码了,也需要注意下访问权限的问题,这里就默认不存在这些问题了,如果你遇到了MySQL的问题不妨百度下,或者留言。

注意:你可能会遇到找不到mysqlclient的错误,下面是解放方法,这种方法只要操作一次就行了。

sudo apt update
sudo apt install libmysqlclient-dev

上面的开发环境是必装的,如果你还是报错可能是缺少一个FindMySQL.cmake文件,这个文件在你的Poco源代码里面的cmake文件夹里面,你只需要复制一份到/usr/share/cmake-<你的版本>/Modules里面就行了,也或者CMAKE_MODULE_PATH变量也行,又或者include(FindMySQL.cmake)也行,看个人喜好。

我的在:/usr/share/cmake-3.22/Modules/FindMySQL.cmake

这里解释下Poco的组件DataMySQL依赖MySQL开发库,但是MySQL开发库是基于PkgConfig的,所以Poco开发了一个FindMySQL.cmake,关键他的其它cmake引用了这个东西,所以单纯改成PkgConfig的方式略有困难,初学者几乎是不太可能完成的事情,所以我找到一个简单的方法那就是直接把文件复制过去,我贴出来文件内容给需要的人。

切记:一定要在加载Poco之前获取MySQL才行,否则会出现找不到MySQL的错误,如果你不懂就按照我的顺序写。

FindMySQL.cmake

# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.

#.rst:
# FindMySQL
# -------
#
# Find MySQL Runtime
#
# This will define the following variables::
#
#   MYSQL_FOUND           - True if the system has the libraries
#   MYSQL_INCLUDE_DIRS    - where to find the headers
#   MYSQL_LIBRARIES       - where to find the libraries
#   MYSQL_DEFINITIONS     - compile definitons
#
# Hints:
# Set ``MYSQL_ROOT_DIR`` to the root directory of an installation.
#
include(FindPackageHandleStandardArgs)

find_package(PkgConfig QUIET)
pkg_check_modules(PC_MYSQL QUIET mysqlclient)
pkg_check_modules(PC_MARIADB QUIET mariadb)

SET(BINDIR32_ENV_NAME "ProgramFiles(x86)")
SET(BINDIR32 $ENV{${BINDIR32_ENV_NAME}})

find_path(MYSQL_INCLUDE_DIR mysql/mysql.h
	HINTS
		${MYSQL_ROOT_DIR}/include
		${MYSQL_ROOT_INCLUDE_DIRS}
	PATHS
		${PC_MYSQL_INCLUDE_DIRS}
		${PC_MARIADB_INCLUDE_DIRS}
		/usr/include
		/usr/local/include
		/opt/mysql/mysql/include
		/usr/local/mysql/include
		$ENV{MYSQL_INCLUDE_DIR}
		$ENV{MYSQL_DIR}/include
		$ENV{ProgramFiles}/MySQL/*/include
		${BINDIR32}/MySQL/*/include
		$ENV{SystemDrive}/MySQL/*/include
		$ENV{MARIADB_INCLUDE_DIR}
		$ENV{MARIADB_DIR}/include
		${MARIADB_INCLUDE_DIR}
		${MARIADB_DIR}/include
	PATH_SUFFIXES
		mysql
		mariadb
)

if (MSVC)
	if (CMAKE_BUILD_TYPE STREQUAL Debug)
		set(libsuffixDist debug)
		set(libsuffixBuild Debug)
	else (CMAKE_BUILD_TYPE STREQUAL Debug)
		set(libsuffixDist opt)
		set(libsuffixBuild Release)
		set(WIN_MYSQL_DEFINITONS " -DDBUG_OFF")
	endif (CMAKE_BUILD_TYPE STREQUAL Debug)

	find_library(MYSQL_LIBRARY NAMES mysqlclient
				 HINTS
					${MYSQL_ROOT_DIR}/lib
					${MYSQL_ROOT_LIBRARY_DIRS}
				 PATHS
					 ${PC_MYSQL_LIBRARY_DIRS}
					 ${PC_MARIADB_LIBRARY_DIRS}
					 $ENV{MYSQL_DIR}/lib
					 $ENV{MYSQL_DIR}/libmysql
					 $ENV{MYSQL_DIR}/client
					 $ENV{ProgramFiles}/MySQL/*/lib
					 ${BINDIR32}/MySQL/*/lib
					 $ENV{SystemDrive}/MySQL/*/lib
				 PATH_SUFFIXES
				 	vs12
				 	vs11
				 	vs10
				 	${libsuffixDist}
				 	${libsuffixBuild}
	)
else()
	find_library(MYSQL_LIBRARY NAMES mysqlclient mysqlclient_r mariadbclient
				 HINTS
					${MYSQL_ROOT_DIR}/lib
					${MYSQL_ROOT_LIBRARY_DIRS}
				 PATHS
					 ${PC_MYSQL_LIBRARY_DIRS}
					 ${PC_MARIADB_LIBRARY_DIRS}
					 /usr/lib
					 /usr/local/lib
					 /usr/local/mysql/lib
					 /opt/mysql/mysql/lib
					 $ENV{MYSQL_DIR}/libmysql_r/.libs
					 $ENV{MYSQL_DIR}/lib
					 ${MYSQL_DIR}/lib
				 PATH_SUFFIXES
					mysql
					mariadb
		)
endif()

set(MYSQL_VERSION ${PC_MYSQL_VERSION})

find_package_handle_standard_args(MySQL
  FOUND_VAR MYSQL_FOUND
  REQUIRED_VARS
	MYSQL_INCLUDE_DIR
	MYSQL_LIBRARY
	VERSION_VAR MYSQL_VERSION
)

if(MYSQL_FOUND)
	set(MYSQL_LIBRARIES ${MYSQL_LIBRARY})
	set(MYSQL_INCLUDE_DIRS ${MYSQL_INCLUDE_DIR})
	set(MYSQL_DEFINITIONS "${PC_MYSQL_CFLAGS_OTHER}${WIN_MYSQL_DEFINITONS}")
endif()

if(MYSQL_FOUND AND NOT TARGET MySQL::client)
  add_library(MySQL::client UNKNOWN IMPORTED)
  set_target_properties(MySQL::client PROPERTIES
	IMPORTED_LOCATION "${MYSQL_LIBRARY}"
	INTERFACE_COMPILE_OPTIONS "${PC_MYSQL_CFLAGS_OTHER}${WIN_MYSQL_DEFINITONS}"
	INTERFACE_INCLUDE_DIRECTORIES "${MYSQL_INCLUDE_DIR}"
  )
endif()

mark_as_advanced(
  MYSQL_LIBRARY
  MYSQL_INCLUDE_DIR
)

CMakeLists.txt

cmake_minimum_required(VERSION 3.21)
project(PoCo_Http_Server)

set(CMAKE_CXX_STANDARD 17)
find_package(Threads REQUIRED)
find_package(MySQL REQUIRED)
find_package(Poco REQUIRED COMPONENTS Foundation Net Util  XML JSON)
add_executable(PoCo_Http_Server main.cpp)

#link threads
target_link_libraries(PoCo_Http_Server Threads::Threads)

find_package(Poco REQUIRED COMPONENTS Data DataMySQL)

#link poco components
target_link_libraries(PoCo_Http_Server Poco::Foundation)
target_link_libraries(PoCo_Http_Server Poco::Net)
target_link_libraries(PoCo_Http_Server Poco::Util)
target_link_libraries(PoCo_Http_Server Poco::XML)
target_link_libraries(PoCo_Http_Server Poco::JSON)
target_link_libraries(PoCo_Http_Server Poco::Data)
target_link_libraries(PoCo_Http_Server Poco::DataMySQL)

main.cpp

#include <iostream>
#include "server/HTTPServerApp.h"

int main(int argc, char **argv) {
	//创建并运行服务器
    HTTPServerApp app;
    return app.run(argc, argv);
}

HTTPServerApp.h

//
// Created by anold on 2024-08-01.
//

#ifndef POCO_HTTP_SERVER_HTTPSERVERAPP_H
#define POCO_HTTP_SERVER_HTTPSERVERAPP_H


#include "../factory/HelloRequestHandlerFactory.h"
#include "../mysql/MySQLSessionPool.h"


class HTTPServerApp : public Poco::Util::ServerApplication {
protected:
    int main(const std::vector<std::string> &args) override {
        //register mysql server
        Poco::Data::MySQL::Connector::registerConnector();
        //create mysql session pool
        MySQLSessionPool::create_session_pool();
        Poco::Net::ServerSocket svs(8080); // 设置 HTTP 服务器端口
        auto *pParams = new Poco::Net::HTTPServerParams;
        pParams->setMaxQueued(100);
        pParams->setMaxThreads(16);
        pParams->setKeepAlive(true);

        Poco::Net::HTTPServer srv(new HelloRequestHandlerFactory, svs, pParams);

        srv.start(); // 启动服务器
        std::cout << "HTTP Server started on port 8080." << std::endl;

        waitForTerminationRequest();  // 等待关闭信号,这个地方会阻塞直到停止信号或崩溃

        srv.stop();  // 停止服务器
        //destroy mysql session pool
        MySQLSessionPool::destroy_session_pool();//unregister前先把会话池回收掉
        //unregister mysql server
        Poco::Data::MySQL::Connector::unregisterConnector();
        return Application::EXIT_OK;
    }
};

#endif //POCO_HTTP_SERVER_HTTPSERVERAPP_H

简单请求:HelloRequestHandlerFactory.h

//
// Created by anold on 2024-08-01.
//

#ifndef POCO_HTTP_SERVER_HELLOREQUESTHANDLERFACTORY_H
#define POCO_HTTP_SERVER_HELLOREQUESTHANDLERFACTORY_H

#include "../request/HelloRequestHandler.h"

class HelloRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
private:
    std::unordered_map<std::string, int> api_map;
    enum request_api {
        ERROR = 0, HELLO
    };

public:
    HelloRequestHandlerFactory() {
        api_map["/hello"] = HELLO;
    }

    Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override {
        auto &uri = request.getURI();
        switch (parser(uri)) {
            case HELLO:
                return new HelloRequestHandler;
            default:
                return new ErrorRequestHandler;
        }
    }

private:
    int parser(const std::string &key) {
        auto it = api_map.find(key);
        if (it != api_map.end())
            return it->second;
        else
            return ERROR;
    }
};

#endif //POCO_HTTP_SERVER_HELLOREQUESTHANDLERFACTORY_H

会话池:MySQLSessionPool.h

//
// Created by anold on 2024-08-02.
//

#ifndef POCO_HTTP_SERVER_MYSQLSESSIONPOOL_H
#define POCO_HTTP_SERVER_MYSQLSESSIONPOOL_H

#include <memory>

#include <Poco/Data/Session.h>
#include <Poco/Data/SessionPool.h>
#include <Poco/Data/MySQL/Connector.h>

using namespace Poco::Data::Keywords;
using Poco::Data::Session;
using Poco::Data::Statement;


class MySQLSessionPool {
public:
    static std::unique_ptr<Poco::Data::SessionPool> pool;

    static void create_session_pool() {
        pool = std::make_unique<Poco::Data::SessionPool>(Poco::Data::MySQL::Connector::KEY,
                                                         "host=localhost;port=3306;"
                                                         "user=debian-sys-maint;password=JEOZXiYplm4blWKV;"
                                                         "db=poco;compress=true;auto_reconnect=true");
    }

    static void destroy_session_pool() {
        pool.reset();
    }
};

std::unique_ptr<Poco::Data::SessionPool> MySQLSessionPool::pool{nullptr};
#endif //POCO_HTTP_SERVER_MYSQLSESSIONPOOL_H

HelloRequestHandler.h

//
// Created by anold on 2024-08-01.
//

#ifndef POCO_HTTP_SERVER_HELLOREQUESTHANDLER_H
#define POCO_HTTP_SERVER_HELLOREQUESTHANDLER_H

#include <Poco/Net/HTTPServer.h>
#include <Poco/Net/HTTPServerParams.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/Util/ServerApplication.h>
#include <Poco/Util/Application.h>
#include <iostream>
#include "../mysql/MySQLSessionPool.h"

class HelloRequestHandler : public Poco::Net::HTTPRequestHandler {
public:
    void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override {
        Poco::Data::Session session(MySQLSessionPool::pool->get());
        if (session.isConnected()) {
//            session << "DROP TABLE IF EXISTS student;", now;
//            session << "CREATE TABLE IF NOT EXISTS student (id INT,name VARCHAR(255),sex VARCHAR(20))", now;
            session << "INSERT INTO student (id,name,sex) VALUES(1,'John','Man')", now;
            std::vector<std::string> names;
            session << "show tables;", into(names), now;
            std::string dbs;
            for (auto &name: names) {
                dbs + name;
                dbs + '\n';
            }
            response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
            response.setContentType("text/html");
            std::ostream &ostr = response.send();
            ostr << "<html><head><title>";
            ostr << dbs;
            ostr << "</title></head>";
            ostr << "<body><h1>Hello from POCO HTTP Server</h1></body></html>";
        }
    }
};

#endif //POCO_HTTP_SERVER_HELLOREQUESTHANDLER_H

ErrorRequestHandller.h

//
// Created by anold on 2024-08-01.
//

#ifndef POCO_HTTP_SERVER_ERRORREQUESTHANDLLER_H
#define POCO_HTTP_SERVER_ERRORREQUESTHANDLLER_H
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <iostream>

class ErrorRequestHandler : public Poco::Net::HTTPRequestHandler {
public:
    void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override {
        response.setStatus(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
        response.setContentType("text/html");
        std::ostream &ostr = response.send();
        ostr << "<h1>Caution Error!</h1>";
    }
};
#endif //POCO_HTTP_SERVER_ERRORREQUESTHANDLLER_H

编译运行就行了!


总结

1、Poco很强大我演示的只是冰山一角,后面我还会演示Poco的其它功能,今天的Demo只能说很简单
2、先预告下JSON模块,线程池,CUID操作和结果获取等等,等这些都学会了开发一个性能不比Spring弱的服务器肯定是足够了。
3、太晚了,睡觉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值