提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
最近接触一个服务器项目,想来想去还是用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.下载压缩包
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、太晚了,睡觉