cppnet
- A lightweight C++ network framework, an upgraded version of cppweb 2.0
- Avoids the bloatiness of the previous cppweb single header file, and uses a more friendly way of using
Author
- chenxuan
Repository Address
Official Website
Docs
Advantages
- Easy to use, low invasiveness, full platform support, does not require forcible installation into the system's include directory, recommended to be directly referenced as a submodule or used directly with static libraries
- Built with modern C++, similar in usage and functions to Go's gin framework, small learning and entry cost, suitable for beginners to learn source code
- Lightweight framework, very small, with less than 5000 lines of source code, avoiding the bloat of large network frameworks
Quick Start
Using the Release Package
- Download the release library (Releases · chenxuan520/cppnet), and extract it (you can choose any location, you can put it in the system's include directory or not. In the following, it is assumed that the extraction is to the current directory). A C++17-compatible compiler is required.
- If you need SSL (you need to install OpenSSL), download the SSL version. The default option is fine.
- Write code. You can write code according to your needs. The following are two simple demos, one without SSL and the other with SSL. For specific function descriptions, you can refer to the documentation.
#include "./cppnet/include/cppnet/http/server/http_server.hpp"
#include "./cppnet/include/cppnet/utils/const.hpp"
#include <iostream>
using namespace cppnet;
int main() {
HttpServer server;
auto rc = server.Init({"127.0.0.1", 8080});
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
server.set_logger(std::make_shared<StdLogger>());
server.GET("/", [](HttpContext &ctx) {
ctx.resp().Success(HttpHeader::ContentType::kTextHtml,
"<h1>Hello, World!</h1>");
});
rc = server.Run();
if (rc != kSuccess) {
std::cout << "run server wrong " << server.err_msg() << std::endl;
return rc;
}
return 0;
}
// ssl 版本
#include "./cppnet/include/cppnet/http/server/http_server.hpp"
#include "./cppnet/include/cppnet/utils/const.hpp"
#include <iostream>
#include <memory>
using namespace cppnet;
int main() {
HttpServer server;
std::shared_ptr<SSLContext> ssl_ctx = std::make_shared<SSLContext>();
auto rc = ssl_ctx->InitSvrFile("./ssl/cacert.pem", "./ssl/privkey.pem");
if (rc != kSuccess) {
std::cout << "init ssl context wrong " << ssl_ctx->err_msg() << std::endl;
return rc;
}
rc = server.InitSSL({"127.0.0.1", 8080}, ssl_ctx);
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
server.set_logger(std::make_shared<StdLogger>());
server.GET("/", [](HttpContext &ctx) {
ctx.resp().Success(HttpHeader::ContentType::kTextHtml,
"<h1>Hello, World!</h1>");
});
rc = server.Run();
if (rc != kSuccess) {
std::cout << "run server wrong " << server.err_msg() << std::endl;
return rc;
}
return 0;
}
-
Compiling the build file
- If you are using CMake to compile, you need to add the following content to the CMakeLists.txt file. In fact, you just add the link library
# Whether to use SSL or not, you need to add this link_directories(./cppnet/lib) link_libraries(-lcppnet) # **If you are using the SSL version and need to use the SSL function, you need to add the following** add_definitions(-DCPPNET_OPENSSL) link_libraries(-lssl -lcrypto)
- If you are using Makefile to compile, you need to add
-L./cppnet/lib
and-lcppnet
to the compilation options. Here is a Makefile compilation demo. Of course, if it is an SSL compilation, you also need to add the lib library and add the CPPNET_OPENSSL macro.
all: libserver-makefile libserver-makefile: g++ -O2 -Wall -std=c++17./main.cpp -o libserver-makefile -lcppnet -L./cppnet/lib # SSL version g++ -O2 -Wall -std=c++17./main.cpp -o libserver-makefile -DCPPNET_OPENSSL -lcppnet -lssl -lcrypto -L./cppnet/lib
-
Execute the compilation to obtain the compiled file successfully.
Compiling from source
- Use
git clone https://github.com/chenxuan520/cppnet --recurse-submodules
to pull the source code- By default, the ssh version is compiled. If you need an ssl-free version, you need to modify the cmake file manually
- Run
cd src;./build.sh
to generate the static library in lib- Requires cmake
- Requires a compiler that supports C++17
Performance Testing
- Automated testing is performed using ubuntu-latest from github action, with machine configuration refer to
- 4 cores, 16GB of memory, and 14GB of hard disk
- Automatically run updated test results based on each commit's code, referring to the scripts in the bench folder, and the hash value will also be in the picture.
- Use vegeta for stress testing, testing at different QPS respectively
- Average response time
- p99 response time
- Accuracy
- Wait time
- Compare cppnet framework and go gin framework, and implement the same function with both
More Demo
- More demo references can be found in the "demo" folder and the "test" folder.
Create a TcpServer
Address addr{"127.0.0.1", 8080};
TcpServer server{addr};
// init server
auto rc = server.Init();
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
// init event function
auto event_func = [&](TcpServer::Event event, TcpServer &server, Socket fd) {
if (event == TcpServer::kEventAccept) {
// accept
std::cout << "accept " << fd.fd() << std::endl;
} else if (event == TcpServer::kEventRead) {
// read
string buf;
auto ser_rc = fd.Read(buf, msg.size());
std::cout << "read " << buf << std::endl;
} else if (event == TcpServer::kEventLeave) {
// leave
std::cout << "leave " << fd.fd() << std::endl;
} else {
// error event
std::cout << "dismiss " << fd.fd() << std::endl;
}
};
// register event function
server.Register(event_func);
Connect to the server using socket
Socket sock;
auto rc = sock.Init();
if (rc != kSuccess) {
std::cout << "init socket wrong " << sock.err_msg() << std::endl;
return rc;
}
rc = sock.Connect(addr);
if (rc != kSuccess) {
std::cout << "connect wrong " << sock.err_msg() << std::endl;
return rc;
}
// write
string msg = "hello world";
rc = sock.Write(msg);
if (rc != kSuccess) {
std::cout << "write wrong " << sock.err_msg() << std::endl;
return rc;
}
// read
string buf;
rc = sock.Read(buf, msg.size());
if (rc <= 0) {
std::cout << "read wrong " << sock.err_msg() << std::endl;
return rc;
}
std::cout << "read " << buf << std::endl;
// close
rc = sock.Close();
if (rc != kSuccess) {
std::cout << "close wrong " << sock.err_msg() << std::endl;
return rc;
}
Create HttpServer
HttpServer server;
auto rc = server.Init({"127.0.0.1", 8080});
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
server.set_logger(std::make_shared<StdLogger>());
server.GET("/", [](HttpContext &ctx) {
ctx.resp().Success(HttpHeader::ContentType::kTextHtml,
"<h1>Hello, World!</h1>");
});
rc = server.Run();
if (rc != kSuccess) {
std::cout << "run server wrong " << server.err_msg() << std::endl;
return rc;
}
Create HttpsServer ssl
HttpServer server;
std::shared_ptr<SSLContext> ssl_ctx = std::make_shared<SSLContext>();
auto rc = ssl_ctx->InitSvrFile("./ssl/cacert.pem", "./ssl/privkey.pem");
if (rc != kSuccess) {
std::cout << "init ssl context wrong " << ssl_ctx->err_msg() << std::endl;
return rc;
}
rc = server.InitSSL({"127.0.0.1", 8080}, ssl_ctx);
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
server.set_logger(std::make_shared<StdLogger>());
server.GET("/", [](HttpContext &ctx) {
ctx.resp().Success(HttpHeader::ContentType::kTextHtml,
"<h1>Hello, World!</h1>");
});
rc = server.Run();
if (rc != kSuccess) {
std::cout << "run server wrong " << server.err_msg() << std::endl;
return rc;
}
Create HttpClient
HttpClient client;
Address addr;
addr.InitWithDomain("www.androidftp.top", 80);
auto rc = client.Init(addr);
// If it is the https 443 port ssl
// auto rc = client.InitSSL(addr);
if (rc != kSuccess) {
std::cout << "init client wrong " << client.err_msg() << std::endl;
return rc;
}
HttpReq req;
req.GET("/");
HttpResp resp;
rc = client.Send(req, resp);
if (rc != kSuccess) {
std::cout << "send request wrong " << client.err_msg() << std::endl;
return rc;
}
std::string resp_str;
rc = resp.Build(resp_str);
if (rc != kSuccess) {
std::cout << "build response wrong " << resp.err_msg() << std::endl;
return rc;
}
Timer trigger server
- To use AddSoc to listen for the timesocket and wait for a trigger.
TcpServer server{addr};
auto rc = server.Init();
if (rc != kSuccess) {
std::cout << "init server wrong " << server.err_msg() << std::endl;
return rc;
}
TimerSocket timerfd(0, 1e5);
if (rc != kSuccess) {
std::cout << "init timerfd wrong " << timerfd.err_msg() << std::endl;
return rc;
}
rc = server.AddSoc(timerfd);
Source code structure
- The repository structure is as follows
- cppnet stores all source code and build files
- lib stores the generated static library and the CMakeLists file for static library construction
- test is the test case
- third_party is a third-party repository, currently only depends on cpptest for unit testing in the test folder
- build.sh generates the lib static library and the unit test binary file
- docs is the interface documentation
- demo is the reference example
- bench is the benchmark
.
├── LICENSE
├── README.en.md
├── README.md
├── bench
│ ├── build.sh
│ └── init.sh
├── demo
│ ├── asset
│ ├── build.sh
│ ├── mnist
│ ├── pack
│ ├── simple-http-server
│ ├── use-lib-server
│ ├── use-lib-server-ssl
│ ├── util
│ └── web-server
├── docs
│ ├── cppnet
│ └── update.sh
└── src
├── CMakeLists.txt
├── build.sh
├── cppnet
├── lib
├── release.sh
├── test
├── third_party
└── win_release.sh
- The source code is mainly placed in
src/cppnet
.
cppnet
├── http
│ ├── client
│ │ ├── http_client.cpp
│ │ └── http_client.hpp
│ ├── header
│ │ ├── http_header.cpp
│ │ └── http_header.hpp
│ ├── req
│ │ ├── http_req.cpp
│ │ ├── http_req.hpp
│ │ ├── method
│ │ │ ├── http_method.cpp
│ │ │ └── http_method.hpp
│ │ └── route
│ │ ├── http_route.cpp
│ │ └── http_route.hpp
│ ├── resp
│ │ ├── http_resp.cpp
│ │ ├── http_resp.hpp
│ │ └── status_code
│ │ ├── http_status_code.cpp
│ │ └── http_status_code.hpp
│ ├── server
│ │ ├── filter
│ │ │ ├── http_filter.hpp
│ │ │ ├── http_host_filter.hpp
│ │ │ └── http_method_filter.hpp
│ │ ├── http_server.cpp
│ │ └── http_server.hpp
│ └── version
│ ├── http_version.cpp
│ └── http_version.hpp
├── log
│ ├── file_logger.cpp
│ ├── file_logger.hpp
│ ├── logger.hpp
│ ├── multi_logger.cpp
│ ├── multi_logger.hpp
│ └── std_logger.hpp
├── server
│ ├── io_multiplexing
│ │ ├── epoll.cpp
│ │ ├── epoll.hpp
│ │ ├── io_multiplexing_base.hpp
│ │ ├── io_multiplexing_factory.cpp
│ │ ├── io_multiplexing_factory.hpp
│ │ ├── kqueue.cpp
│ │ ├── kqueue.hpp
│ │ ├── select.cpp
│ │ └── select.hpp
│ ├── tcp_server.cpp
│ └── tcp_server.hpp
├── socket
│ ├── address.cpp
│ ├── address.hpp
│ ├── socket.cpp
│ └── socket.hpp
├── ssl
│ ├── ssl_context.cpp
│ ├── ssl_context.hpp
│ ├── ssl_socket.cpp
│ └── ssl_socket.hpp
├── timer
│ ├── timer.cpp
│ └── timer.hpp
└── utils
├── const.hpp
├── file.cpp
├── file.hpp
├── host.cpp
├── host.hpp
├── string.cpp
├── string.hpp
├── threadpool.cpp
├── threadpool.hpp
├── trie.cpp
├── trie.hpp
├── version.cpp
└── version.hpp
19 directories, 61 files