├── .gitignore ├── .vscode └── launch.json ├── CMakeLists.txt ├── Flowchart.png ├── config └── config.ini ├── file_upload1.png ├── file_upload2.png ├── file_upload3.png ├── index.png ├── log └── server.log ├── readme.md ├── src ├── CMakeLists.txt ├── Task │ ├── CMakeLists.txt │ ├── http_task.cpp │ ├── http_task.h │ ├── task_factory.cpp │ └── task_factory.h ├── app │ ├── CMakeLists.txt │ ├── file.cpp │ ├── file.h │ ├── index.cpp │ ├── index.h │ ├── user.cpp │ └── user.h ├── frame │ ├── CMakeLists.txt │ ├── server.cpp │ └── server.h ├── jsonParser │ ├── CMakeLists.txt │ ├── Json.cpp │ ├── Json.h │ ├── Parser.cpp │ └── Parser.h ├── main.cpp ├── reflect │ ├── CMakeLists.txt │ ├── class_factory.cpp │ ├── class_factory.h │ ├── class_field.h │ ├── class_method.h │ └── class_register.h ├── socketHandler │ ├── CMakeLists.txt │ ├── event_poller.cpp │ ├── event_poller.h │ ├── socket.cpp │ ├── socket.h │ ├── socket_handler.cpp │ └── socket_handler.h ├── thread │ ├── CMakeLists.txt │ ├── task.cpp │ ├── task.h │ ├── task_dispatcher.cpp │ ├── task_dispatcher.h │ ├── thread.cpp │ ├── thread.h │ ├── thread_pool.cpp │ ├── thread_pool.h │ ├── worker_thread.cpp │ └── worker_thread.h ├── utility │ ├── CMakeLists.txt │ ├── Logger.cpp │ ├── Logger.h │ ├── Singleton.h │ ├── String.cpp │ ├── String.h │ ├── Value.cpp │ ├── Value.h │ ├── iniFile.cpp │ ├── iniFile.h │ ├── system.cpp │ └── system.h └── web │ ├── CMakeLists.txt │ ├── controller.h │ ├── file_upload.cpp │ ├── file_upload.h │ ├── request.cpp │ ├── request.h │ ├── response.cpp │ ├── response.h │ ├── server.cpp │ └── server.h ├── static ├── indexstyles.css ├── logo.jpg ├── man.jpg ├── styles.css ├── upload.js └── woman.jpg ├── template ├── 404.html ├── file │ └── index.html ├── index │ ├── hello.html │ └── index.html └── user │ ├── all.html │ ├── happy.html │ └── index.html └── test ├── CMakeLists.txt └── web_parse_request ├── CMakeLists.txt ├── http_request.txt └── request_test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /build 3 | .cache 4 | *.a 5 | *.so 6 | *.log 7 | .vscode 8 | /upload 9 | *.log -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) 启动", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/bin/Server", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "为 gdb 启用整齐打印", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | }, 24 | { 25 | "description": "将反汇编风格设置为 Intel", 26 | "text": "-gdb-set disassembly-flavor intel", 27 | "ignoreFailures": true 28 | } 29 | ] 30 | } 31 | 32 | ] 33 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(tiny-webserver 3 | VERSION 0.0.1 4 | DESCRIPTION "使用C++实现一个简易的webserver" 5 | HOMEPAGE_URL "https://github.com/breeze-wink/tiny-Webserver" 6 | LANGUAGES CXX 7 | ) 8 | 9 | # 设置C++标准为17 10 | set(CMAKE_CXX_STANDARD 17) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | 13 | enable_testing() 14 | 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) 16 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) 17 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib) 18 | 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -g -fPIC") 20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") 21 | 22 | set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 23 | 24 | add_subdirectory(src) 25 | add_subdirectory(test) 26 | -------------------------------------------------------------------------------- /Flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/Flowchart.png -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | [server] 2 | ip = 0.0.0.0 3 | port = 8080 4 | threads = 64 5 | max_conns = 65535 6 | wait_time = 10 7 | log_level = 0 -------------------------------------------------------------------------------- /file_upload1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/file_upload1.png -------------------------------------------------------------------------------- /file_upload2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/file_upload2.png -------------------------------------------------------------------------------- /file_upload3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/file_upload3.png -------------------------------------------------------------------------------- /index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/index.png -------------------------------------------------------------------------------- /log/server.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/log/server.log -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # WebServer 2 | 3 | ## 相关技术 4 | - C++11~C++17的一些新特性使用 5 | - tcp连接, http请求, 响应 6 | - MVC架构模式:处理任务的模型,返回的视图,控制器彼此松耦合。 7 | - 高并发:使用epoll事件驱动模型, Reactor模式、线程池 8 | - 使用CMake构建程序 9 | - 设计模式:单例模式、工厂模式 10 | - 不依赖外部库实现的反射、日志系统、ini解析、json解析、动态数据类型等 11 | ## 已实现GET、POST请求功能演示 12 | 13 | ### GET请求功能演示 14 | 可根据URL地址访问,并返回相应的页面。 15 | 16 | 17 | 18 | ### POST请求功能演示(实现了json和file上传功能) 19 | 20 | 21 | 22 | 23 | 24 | ## 各线程工作 25 | 26 | 1. **主线程 (SocketHandler)**:采用reactor的事件处理模型,使用epoll来监听和处理socket连接,产生特定任务交给任务分发线程。 27 | 2. **任务分发线程**:负责接收任务,将任务对接给线程池。 28 | 3. **线程池**:管理的一批工作线程,当线程池接到任务时,会调度当前空闲的工作线程来处理任务。 29 | 30 | ## 从socket连接到处理结束,各组件之间的配合 31 | 32 | 1. **SocketHandler** 接收到socket连接请求,从 **TaskFactory** 获取一个 **HttpTask**。 33 | 2. **SocketHandler** 将 **HttpTask** 交给 **task_dispatcher**,后者将 **HttpTask** 交给线程池。 34 | 3. 线程池将 **HttpTask** 分配给工作线程,线程开始进行 **HttpTask** 处理。 35 | 4. 在 **HttpTask** 中,接收HTTP请求,并调用 **Web** 组件中的 **Request** 解析,分别解析header和body。 36 | 5. **Request** 解析完毕后,交给 **Web** 组件中的 **Server**, **Server** 负责处理 **Request**,自动路由,生成相应的 **Response**。 37 | 支持处理的类型有三种: 38 | - a. 静态绑定处理方法与具体的path。 39 | - b. 直接请求具体路径的文件,支持html、js、png、jpg等。 40 | - c. 基于反射注册的控制器和方法,与 **App** 组件交互。 41 | 6. **Request** 和 **Response** 中涉及的json文本处理由 **JsonParser** 组件进行。 42 | 7. **Response** 生成完毕后,在 **HttpTask** 中发送回客户端。 43 | 8. 贯穿始终的动态数据类型、日志、字符串、ini配置文件解析功能等由 **Utility** 组件提供。 44 | 45 | **大致的流程图如下** 46 | ![alt text](Flowchart.png) 47 | 48 | ## 压力测试 49 | - **RPS:** 使用开源工具[wrk](https://github.com/wg/wrk)测试,测试环境为Ubuntu64虚拟机,相同环境下与nginx进行对比,每秒http请求处理数量(RPS)达到nginx的70%左右。 50 | - **文件上传:** 经过简单测试,可以稳定上传1.5G以内的文件。 -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(socketHandler_Dir ${CMAKE_CURRENT_SOURCE_DIR}/socketHandler) 2 | set(utility_Dir ${CMAKE_CURRENT_SOURCE_DIR}/utility) 3 | set(ThreadPool_Dir ${CMAKE_CURRENT_SOURCE_DIR}/thread) 4 | set(frame_Dir ${CMAKE_CURRENT_SOURCE_DIR}/frame) 5 | set(task_Dir ${CMAKE_CURRENT_SOURCE_DIR}/Task) 6 | set(jsonParser_Dir ${CMAKE_CURRENT_SOURCE_DIR}/jsonParser) 7 | set(web_Dir ${CMAKE_CURRENT_SOURCE_DIR}/web) 8 | set(reflect_Dir ${CMAKE_CURRENT_SOURCE_DIR}/reflect) 9 | set(app_Dir ${CMAKE_CURRENT_SOURCE_DIR}/app) 10 | 11 | 12 | add_subdirectory(${utility_Dir}) 13 | add_subdirectory(${ThreadPool_Dir}) 14 | add_subdirectory(${frame_Dir}) 15 | add_subdirectory(${task_Dir}) 16 | add_subdirectory(${jsonParser_Dir}) 17 | add_subdirectory(${reflect_Dir}) 18 | add_subdirectory(${web_Dir}) 19 | add_subdirectory(${socketHandler_Dir}) 20 | add_subdirectory(${app_Dir}) 21 | 22 | 23 | add_executable(Server main.cpp) 24 | 25 | target_include_directories(Server PUBLIC ${web_Dir} ${utility_Dir}) 26 | 27 | target_link_libraries(Server PUBLIC frame app) 28 | 29 | set_target_properties(Server PROPERTIES 30 | BUILD_RPATH ${PROJECT_SOURCE_DIR}/lib 31 | ) -------------------------------------------------------------------------------- /src/Task/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(task 2 | DESCRIPTION "实现各种业务需要的任务" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 7 | 8 | target_include_directories(${PROJECT_NAME} PUBLIC ${socketHandler_Dir} 9 | ${utility_Dir} ${ThreadPool_Dir} 10 | ${web_Dir} ${jsonParser_Dir}) 11 | 12 | target_link_libraries(${PROJECT_NAME} PUBLIC ThreadPool) -------------------------------------------------------------------------------- /src/Task/http_task.cpp: -------------------------------------------------------------------------------- 1 | #include "http_task.h" 2 | #include "Logger.h" 3 | #include "response.h" 4 | #include "socket.h" 5 | #include "server.h" 6 | #include "socket_handler.h" 7 | #include "request.h" 8 | #include "task_factory.h" 9 | #include 10 | #include 11 | 12 | using namespace breeze::socket; 13 | using namespace breeze::task; 14 | using namespace breeze::web; 15 | 16 | HttpTask::HttpTask(int sockfd) : Task(), m_sockfd(sockfd) 17 | { 18 | m_total_len = 0; 19 | m_head_len = 0; 20 | m_body_len = 0; 21 | m_body_idx = 0; 22 | m_body = nullptr; 23 | } 24 | HttpTask::~HttpTask() 25 | { 26 | 27 | } 28 | void HttpTask::run() 29 | { 30 | log_debug("http task run"); 31 | 32 | char buf[recv_buff_size] = {0}; 33 | Socket socket(m_sockfd); 34 | int len = socket.recv(buf, sizeof(buf)); 35 | 36 | if (len < 0) 37 | { 38 | if (errno == EAGAIN || errno == EWOULDBLOCK) //阻塞模式下,客⼾端异常退出处理, 非阻塞下,recv读空,send写满缓冲区会触发 39 | { 40 | log_debug("socket recv/send would block: conn = %d", m_sockfd); 41 | return; //注意,这是正常的 42 | } 43 | 44 | //interrupt 45 | else if (errno == EINTR) //读写出现中断错误 46 | { 47 | log_error("socket recv interrupted: conn = %d", m_sockfd); 48 | return; 49 | } 50 | 51 | log_error("socket connection abort: conn = %d", m_sockfd); 52 | m_closed = true; 53 | return; 54 | } 55 | 56 | if (len == 0) 57 | { 58 | log_debug("socket closed by peer : conn = %d", m_sockfd); 59 | m_closed = true; 60 | return; 61 | } 62 | log_debug("recv: conn = %d, msg = \n%s", m_sockfd, buf); 63 | 64 | m_total_len += len; 65 | if (m_head_len == 0) 66 | { 67 | m_head_len = m_req.parse_header(buf, len); 68 | m_body_len = m_req.content_length(); 69 | 70 | if (m_body_len > 0) 71 | { 72 | m_body = new char[m_body_len + 1]; 73 | m_body[m_body_len] = '\0'; 74 | memcpy(m_body, buf + m_head_len, len - m_head_len); 75 | m_body_idx = len - m_head_len; 76 | } 77 | } 78 | 79 | else //不是第一次,都是消息体里面的内容 80 | { 81 | memcpy(m_body + m_body_idx, buf, len); 82 | m_body_idx += len; 83 | } 84 | 85 | if (m_total_len - m_head_len >= m_body_len) 86 | { 87 | //消息接收完整 88 | if (m_body_len > 0) 89 | { 90 | m_req.parse_body(m_body, m_body_len); 91 | } 92 | string resp = Singleton::Instance() -> handle(m_req); 93 | socket.send(resp.c_str(), resp.size()); 94 | 95 | reset(); 96 | } 97 | } 98 | 99 | void HttpTask::destroy() 100 | { 101 | log_debug("http task destroy"); 102 | if (m_closed) 103 | { 104 | Singleton::Instance() -> remove(m_sockfd); 105 | } 106 | else 107 | { 108 | Singleton::Instance() -> attach(m_sockfd); 109 | } 110 | } 111 | 112 | void HttpTask::reset() 113 | { 114 | m_total_len = 0; 115 | m_head_len = 0; 116 | m_body_len = 0; 117 | m_body_idx = 0; 118 | 119 | if (m_body != nullptr) 120 | { 121 | delete [] m_body; 122 | m_body = nullptr; 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /src/Task/http_task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task.h" 3 | #include "request.h" 4 | using namespace breeze::thread; 5 | using namespace breeze::web; 6 | namespace breeze 7 | { 8 | namespace task 9 | { 10 | 11 | const uint32_t recv_buff_size = 1024 * 8; 12 | 13 | class HttpTask : public Task 14 | { 15 | public: 16 | HttpTask() = delete; 17 | HttpTask(int sockfd); 18 | ~HttpTask(); 19 | 20 | void reset(); 21 | 22 | virtual void run(); 23 | virtual void destroy(); 24 | 25 | private: 26 | int m_sockfd = 0; 27 | bool m_closed = false; 28 | Request m_req; 29 | int m_total_len; 30 | int m_head_len; 31 | long m_body_len; 32 | int m_body_idx; 33 | char* m_body; 34 | }; 35 | } 36 | } -------------------------------------------------------------------------------- /src/Task/task_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "task_factory.h" 2 | #include "Logger.h" 3 | #include "http_task.h" 4 | #include 5 | #include 6 | 7 | using namespace breeze::task; 8 | 9 | std::shared_ptr TaskFactory::create(int sockfd) 10 | { 11 | std::unique_lock lock(m_mutex); 12 | auto it = m_sock_task.find(sockfd); 13 | if (it != m_sock_task.end()) 14 | { 15 | log_debug("task_factory return a existing task: %x", it -> second.get()); 16 | return it -> second; 17 | } 18 | std::shared_ptr task = std::make_shared(sockfd); 19 | 20 | m_sock_task[sockfd] = task; 21 | 22 | log_debug("task_factory return a new task: %x", task.get()); 23 | return task; 24 | } 25 | 26 | void TaskFactory::remove(int sockfd) 27 | { 28 | std::unique_lock lock(m_mutex); 29 | auto it = m_sock_task.find(sockfd); 30 | 31 | if (it != m_sock_task.end()) 32 | { 33 | m_sock_task.erase(it); 34 | } 35 | 36 | close(sockfd); 37 | } -------------------------------------------------------------------------------- /src/Task/task_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "task.h" 3 | #include "Singleton.h" 4 | #include 5 | #include 6 | #include 7 | // #include "echo_task.h" 8 | // #include "work_task.h" 9 | 10 | using namespace breeze::thread; 11 | using namespace breeze::utility; 12 | 13 | namespace breeze::task 14 | { 15 | class TaskFactory 16 | { 17 | SINGLETON(TaskFactory); 18 | public: 19 | //TODO: 完善工厂逻辑 20 | std::shared_ptr create(int sockfd); 21 | void remove(int sockfd); 22 | 23 | private: 24 | std::map> m_sock_task; 25 | std::mutex m_mutex; 26 | }; 27 | } -------------------------------------------------------------------------------- /src/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(app) 2 | 3 | aux_source_directory(. DIR_SRC) 4 | 5 | add_library(${PROJECT_NAME} SHARED ${DIR_SRC}) 6 | 7 | target_include_directories(${PROJECT_NAME} PUBLIC ${web_Dir}) 8 | target_link_libraries(${PROJECT_NAME} PUBLIC web) 9 | 10 | -------------------------------------------------------------------------------- /src/app/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.h" 2 | #include "file_upload.h" 3 | #include "system.h" 4 | using namespace breeze::app; 5 | 6 | CONTROLLER(File); 7 | ACTION(File, index); 8 | ACTION(File, upload); 9 | 10 | 11 | void File::index(const Request& req, Response& resp) 12 | { 13 | resp.render("file/index.html"); 14 | } 15 | 16 | void File::upload(const Request& req, Response& resp) 17 | { 18 | FileUpload file = req.file("file"); 19 | 20 | auto sys = Singleton::Instance(); 21 | 22 | const string path = sys -> get_root_path() + "/upload/" + file.filename(); 23 | 24 | file.save(path); 25 | 26 | Json json; 27 | json["message"] = "文件上传成功"; 28 | json["filename"] = file.filename(); 29 | json["size"] = file.size(); 30 | 31 | resp.json(json.str()); 32 | } -------------------------------------------------------------------------------- /src/app/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controller.h" 4 | 5 | #include "request.h" 6 | #include "response.h" 7 | 8 | using namespace breeze::web; 9 | 10 | namespace breeze::app 11 | { 12 | class File : public Controller 13 | { 14 | public: 15 | void index(const Request& req, Response& resp); 16 | void upload(const Request& req, Response& resp); 17 | }; 18 | } -------------------------------------------------------------------------------- /src/app/index.cpp: -------------------------------------------------------------------------------- 1 | #include "index.h" 2 | 3 | using namespace breeze::app; 4 | 5 | CONTROLLER(Index); 6 | ACTION(Index, index); 7 | ACTION(Index, hello); 8 | 9 | 10 | void Index::index(const Request& req, Response& resp) 11 | { 12 | resp.render("index/index.html"); 13 | } 14 | 15 | void Index::hello(const Request& req, Response& resp) 16 | { 17 | resp.render("index/hello.html"); 18 | } -------------------------------------------------------------------------------- /src/app/index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "controller.h" 4 | 5 | #include "request.h" 6 | #include "response.h" 7 | 8 | using namespace breeze::web; 9 | 10 | namespace breeze::app 11 | { 12 | class Index : public Controller 13 | { 14 | public: 15 | void index(const Request& req, Response& resp); 16 | void hello(const Request& req, Response& resp); 17 | }; 18 | } -------------------------------------------------------------------------------- /src/app/user.cpp: -------------------------------------------------------------------------------- 1 | #include "user.h" 2 | 3 | 4 | using namespace breeze::app; 5 | 6 | CONTROLLER(User); 7 | ACTION(User, index); 8 | ACTION(User, tryall); 9 | ACTION(User, happy); 10 | 11 | void User::index(const Request& req, Response& resp) 12 | { 13 | resp.render("user/index.html"); 14 | } 15 | 16 | void User::tryall(const Request& req, Response& resp) 17 | { 18 | resp.render("user/all.html"); 19 | } 20 | 21 | void User::happy(const Request& req, Response& resp) 22 | { 23 | resp.render("user/happy.html"); 24 | } -------------------------------------------------------------------------------- /src/app/user.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "controller.h" 3 | #include "request.h" 4 | #include "response.h" 5 | 6 | using namespace breeze::web; 7 | 8 | namespace breeze::app 9 | { 10 | class User : public Controller 11 | { 12 | public: 13 | void index(const Request& req, Response& resp); 14 | void tryall(const Request& req, Response& resp); 15 | void happy(const Request& req, Response& resp); 16 | }; 17 | } -------------------------------------------------------------------------------- /src/frame/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(frame 2 | DESCRIPTION "将server的启动进行一个封装" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | 7 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 8 | target_include_directories(${PROJECT_NAME} PUBLIC ${utility_Dir} ${socketHandler_Dir} ${ThreadPool_Dir}) 9 | target_link_libraries(${PROJECT_NAME} PUBLIC utility socketHandler ThreadPool) -------------------------------------------------------------------------------- /src/frame/server.cpp: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include "system.h" 3 | #include "iniFile.h" 4 | #include "Logger.h" 5 | #include "task_dispatcher.h" 6 | #include "socket_handler.h" 7 | 8 | using namespace breeze::frame; 9 | using namespace breeze::socket; 10 | using namespace breeze::thread; 11 | 12 | void Server::start() 13 | { 14 | auto sys = Singleton::Instance(); 15 | sys -> init(); 16 | 17 | string root_path = sys -> get_root_path(); 18 | string config_file_path = root_path + "config/config.ini"; 19 | string server_log_path = root_path + "log/server.log"; 20 | 21 | auto ini = Singleton::Instance(); 22 | ini -> load(config_file_path); 23 | 24 | m_ip = (string)ini -> get("server", "ip"); 25 | m_port = ini -> get("server", "port"); 26 | m_threads = ini -> get("server", "threads"); 27 | m_max_conns = ini -> get("server", "max_conns"); 28 | m_wait_time = ini -> get("server", "wait_time"); 29 | m_log_level = ini -> get("server", "log_level"); 30 | 31 | auto logger = Singleton::Instance(); 32 | logger -> open(server_log_path); 33 | logger -> setConsole(false); 34 | logger -> setLevel(static_cast(m_log_level)); 35 | 36 | auto dispatcher = Singleton::Instance(); 37 | dispatcher -> init(m_threads); 38 | 39 | auto handle = Singleton::Instance(); 40 | handle -> listen(m_ip, m_port); 41 | handle -> handle(m_max_conns, m_wait_time); 42 | } -------------------------------------------------------------------------------- /src/frame/server.h: -------------------------------------------------------------------------------- 1 | #include "Singleton.h" 2 | #include 3 | 4 | using namespace breeze::utility; 5 | using std::string; 6 | 7 | namespace breeze::frame 8 | { 9 | class Server 10 | { 11 | SINGLETON(Server); 12 | public: 13 | void start(); 14 | 15 | private: 16 | string m_ip = ""; 17 | int m_port = 0; 18 | int m_threads = 0; 19 | int m_max_conns = 0; 20 | int m_wait_time = 0; 21 | int m_log_level = 0; 22 | }; 23 | } -------------------------------------------------------------------------------- /src/jsonParser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(jsonParser 2 | DESCRIPTION "实现了一个json文件的解析器" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | 7 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 8 | 9 | -------------------------------------------------------------------------------- /src/jsonParser/Json.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | #include "Json.h" 3 | #include 4 | 5 | using namespace breeze::json; 6 | 7 | Json::Json() : m_type(JSON_NULL) {} 8 | 9 | Json::Json(Json::Type type) : m_type(type) { 10 | 11 | switch (m_type) { 12 | case JSON_NULL: 13 | break; 14 | case JSON_BOOL: 15 | m_value.m_bool = false; 16 | break; 17 | case JSON_INT: 18 | m_value.m_int = 0; 19 | break; 20 | case JSON_DOUBLE: 21 | m_value.m_double = 0.0; 22 | break; 23 | case JSON_STRING: 24 | m_value.m_string = new string(""); 25 | break; 26 | case JSON_ARRAY: 27 | m_value.m_array = new std::vector(); 28 | break; 29 | case JSON_OBJECT: 30 | m_value.m_object = new std::map(); 31 | break; 32 | default: 33 | break; 34 | } 35 | } 36 | 37 | Json::Json(bool value) : m_type(JSON_BOOL) { 38 | m_value.m_bool = value; 39 | } 40 | 41 | Json::Json(int value) : m_type(JSON_INT) { 42 | m_value.m_int = value; 43 | } 44 | 45 | Json::Json(double value) : m_type(JSON_DOUBLE) { 46 | m_value.m_double = value; 47 | } 48 | 49 | Json::Json(const char *value) : m_type(JSON_STRING) { 50 | m_value.m_string = new string(value); 51 | } 52 | 53 | Json::Json(const string &value) : m_type(JSON_STRING) { 54 | m_value.m_string = new string(value); 55 | } 56 | 57 | Json::~Json() { 58 | clear(); 59 | } 60 | 61 | string Json::str() const { 62 | std::ostringstream oss; 63 | switch (m_type) { 64 | case JSON_NULL: 65 | oss << "null"; 66 | break; 67 | case JSON_BOOL: { 68 | if (m_value.m_bool) { 69 | oss << "true"; 70 | } else { 71 | oss << "false"; 72 | } 73 | break; 74 | } 75 | case JSON_INT: 76 | oss << m_value.m_int; 77 | break; 78 | case JSON_DOUBLE: 79 | oss << m_value.m_double; 80 | break; 81 | case JSON_STRING: 82 | oss << "\"" + *m_value.m_string + "\""; 83 | break; 84 | case JSON_ARRAY: 85 | oss << "["; 86 | for (auto it = (m_value.m_array)->begin(); it != m_value.m_array->end(); ++it) { 87 | if (it != m_value.m_array->begin()) { 88 | oss << ","; 89 | } 90 | oss << (*it).str(); 91 | } 92 | oss << "]"; 93 | break; 94 | case JSON_OBJECT: 95 | oss << "{"; 96 | for (auto it = m_value.m_object->begin(); it != m_value.m_object->end(); ++it) { 97 | if (it != m_value.m_object->begin()) { 98 | oss << ","; 99 | } 100 | oss << "\"" << it->first << "\"" << ":" << it->second.str(); 101 | } 102 | oss << "}"; 103 | break; 104 | default: 105 | break; 106 | } 107 | return oss.str(); 108 | } 109 | 110 | 111 | Json::Type Json::type() const { 112 | return m_type; 113 | } 114 | 115 | bool Json::is_null() const { 116 | return m_type == JSON_NULL; 117 | } 118 | 119 | bool Json::is_bool() const { 120 | return m_type == JSON_BOOL; 121 | } 122 | 123 | bool Json::is_int() const { 124 | return m_type == JSON_INT; 125 | } 126 | 127 | bool Json::is_double() const { 128 | return m_type == JSON_DOUBLE; 129 | } 130 | 131 | bool Json::is_string() const { 132 | return m_type == JSON_STRING; 133 | } 134 | 135 | bool Json::is_array() const { 136 | return m_type == JSON_ARRAY; 137 | } 138 | 139 | bool Json::is_object() const { 140 | return m_type == JSON_OBJECT; 141 | } 142 | 143 | bool Json::as_bool() const { 144 | if (m_type != JSON_BOOL) { 145 | throw std::logic_error("type error: not bool type"); 146 | } 147 | return m_value.m_bool; 148 | } 149 | 150 | int Json::as_int() const { 151 | if (m_type != JSON_INT) { 152 | throw std::logic_error("type error: not int type"); 153 | } 154 | return m_value.m_int; 155 | } 156 | 157 | double Json::as_double() const { 158 | if (m_type != JSON_DOUBLE) { 159 | throw std::logic_error("type error: not double type"); 160 | } 161 | return m_value.m_double; 162 | } 163 | 164 | string Json::as_string() const { 165 | if (m_type != JSON_STRING) { 166 | throw std::logic_error("type error: not string type"); 167 | } 168 | 169 | return *m_value.m_string; 170 | } 171 | 172 | Json::operator bool() { 173 | return as_bool(); 174 | } 175 | 176 | Json::operator bool() const { 177 | return as_bool(); 178 | } 179 | 180 | Json::operator int() { 181 | return as_int(); 182 | } 183 | 184 | Json::operator int() const { 185 | return as_int(); 186 | } 187 | 188 | Json::operator double() { 189 | return as_double(); 190 | } 191 | 192 | Json::operator double() const { 193 | return as_double(); 194 | } 195 | 196 | Json::operator string() { 197 | return as_string(); 198 | } 199 | 200 | Json::operator string() const { 201 | return as_string(); 202 | } 203 | 204 | Json &Json::operator=(bool value) { 205 | clear();//删去旧的内存 206 | m_type = JSON_BOOL; 207 | m_value.m_bool = value; 208 | return *this; 209 | } 210 | 211 | Json &Json::operator=(int value) { 212 | clear(); 213 | m_type = JSON_INT; 214 | m_value.m_int = value; 215 | return *this; 216 | } 217 | 218 | Json &Json::operator=(double value) { 219 | clear(); 220 | m_type = JSON_DOUBLE; 221 | m_value.m_double = value; 222 | return *this; 223 | } 224 | 225 | Json &Json::operator=(const char *value) { 226 | clear(); 227 | m_type = JSON_STRING; 228 | m_value.m_string = new string(value); 229 | return *this; 230 | } 231 | 232 | Json &Json::operator=(const string &value) { 233 | clear(); 234 | m_type = JSON_STRING; 235 | m_value.m_string = new string(value); 236 | return *this; 237 | } 238 | 239 | void Json::clear() { 240 | switch (m_type) { 241 | case JSON_NULL: 242 | case JSON_INT: 243 | case JSON_BOOL: 244 | case JSON_DOUBLE: 245 | break; 246 | case JSON_STRING: 247 | if (m_value.m_string != nullptr) { 248 | delete m_value.m_string; 249 | m_value.m_string = nullptr; 250 | } 251 | break; 252 | case JSON_ARRAY: 253 | if (m_value.m_array != nullptr) { 254 | auto &vec = m_value.m_array; 255 | for (auto &it: *vec) { 256 | it.clear(); 257 | } 258 | delete vec; 259 | vec = nullptr; 260 | } 261 | case JSON_OBJECT: 262 | if (m_value.m_object != nullptr) { 263 | auto &map = m_value.m_object; 264 | for (auto &it: *map) { 265 | it.second.clear(); 266 | } 267 | delete map; 268 | map = nullptr; 269 | } 270 | default: 271 | break; 272 | } 273 | m_type = JSON_NULL; 274 | } 275 | 276 | void Json::append(const Json &value) { 277 | if (m_type != JSON_ARRAY) { 278 | clear(); 279 | m_type = JSON_ARRAY; 280 | m_value.m_array = new std::vector(); 281 | } 282 | m_value.m_array->push_back(value); 283 | } 284 | 285 | bool Json::has(int index) const{ 286 | if (m_type != JSON_ARRAY) { 287 | return false; 288 | } 289 | int size = static_cast(m_value.m_array->size()); 290 | return index >= 0 && index < size; 291 | } 292 | 293 | Json Json::get(int index) const{ 294 | if (!has(index)) { 295 | throw std::logic_error("out_of_range"); 296 | } 297 | return m_value.m_array->at(index); 298 | } 299 | 300 | void Json::remove(int index) { 301 | if (!has(index)) { 302 | throw std::logic_error("out_of_range"); 303 | } 304 | auto &vec = m_value.m_array; 305 | vec->at(index).clear(); 306 | vec->erase(vec->begin() + index); 307 | } 308 | 309 | Json &Json::operator[](int index) { 310 | if (m_type != JSON_ARRAY) { 311 | throw std::logic_error("type error: not array"); 312 | } 313 | if (!has(index)) { 314 | throw std::logic_error("array out of range"); 315 | } 316 | return m_value.m_array->at(index); 317 | } 318 | 319 | Json Json::get(const char *key) const{ 320 | if (!has(key)) { 321 | return Json(); 322 | } 323 | return (*m_value.m_object)[key]; 324 | } 325 | 326 | Json Json::get(const string &key) const { 327 | return get(key.c_str()); 328 | } 329 | 330 | void Json::remove(const char *key) { 331 | if (m_type != JSON_OBJECT) { 332 | return; 333 | } 334 | auto it = m_value.m_object->find(key); 335 | if (it == m_value.m_object->end()) { 336 | return; 337 | } 338 | it->second.clear(); 339 | m_value.m_object->erase(it); 340 | } 341 | 342 | void Json::remove(const string &key) { 343 | remove(key.c_str()); 344 | } 345 | 346 | Json &Json::operator[](const char *key) { 347 | if (m_type != JSON_OBJECT) { 348 | clear(); 349 | m_type = JSON_OBJECT; 350 | m_value.m_object = new std::map(); 351 | } 352 | 353 | return (*m_value.m_object)[key]; 354 | } 355 | 356 | Json &Json::operator[](const string &key) { 357 | return (*this)[key.c_str()]; 358 | } 359 | 360 | bool Json::has(const char *key) const { 361 | if (m_type != JSON_OBJECT) { 362 | return false; 363 | } 364 | return m_value.m_object->find(key) != m_value.m_object->end(); 365 | } 366 | 367 | bool Json::has(const string &key) const { 368 | return has(key.c_str()); 369 | } 370 | 371 | int Json::size() const { 372 | switch (m_type) { 373 | case JSON_ARRAY: 374 | return (int) (m_value.m_array)->size(); 375 | case JSON_OBJECT: 376 | return (int) (m_value.m_object)->size(); 377 | default: 378 | break; 379 | } 380 | return -1; 381 | } 382 | 383 | bool Json::empty() const { 384 | switch (m_type) { 385 | case JSON_NULL: 386 | return true; 387 | case JSON_ARRAY: 388 | return m_value.m_array->empty(); 389 | case JSON_OBJECT: 390 | return m_value.m_object->empty(); 391 | default: 392 | break; 393 | } 394 | return false; 395 | } 396 | 397 | Json::Json(const Json &other) { 398 | copy(other); 399 | } 400 | 401 | Json &Json::operator=(const Json &other) { 402 | copy(other); 403 | return *this; 404 | } 405 | 406 | void Json::copy(const Json &other) //深拷贝 407 | { 408 | clear(); 409 | m_type = other.type(); 410 | switch (m_type) { 411 | case JSON_NULL: 412 | break; 413 | case JSON_BOOL: 414 | case JSON_INT: 415 | case JSON_DOUBLE: 416 | m_value = other.m_value; 417 | break; 418 | case JSON_STRING: 419 | if (other.m_value.m_string != nullptr) { 420 | m_value.m_string = new string(*other.m_value.m_string); 421 | } 422 | break; 423 | case JSON_ARRAY: 424 | if (other.m_value.m_array != nullptr) { 425 | m_value.m_array = new std::vector(); 426 | for (auto &json: *other.m_value.m_array) { 427 | m_value.m_array->push_back(json); 428 | } 429 | } 430 | break; 431 | case JSON_OBJECT: 432 | if (other.m_value.m_object != nullptr) { 433 | m_value.m_object = new std::map(); 434 | for (auto &pair: *other.m_value.m_object) { 435 | m_value.m_object->insert(std::make_pair(pair.first, pair.second)); 436 | } 437 | } 438 | break; 439 | default: 440 | break; 441 | } 442 | } 443 | 444 | void Json::parse(const string &file) { 445 | clear(); 446 | Parser parser; 447 | parser.load(file); 448 | *this = parser.parse(); 449 | } 450 | 451 | void Json::parse(const char *buf, int len) { 452 | clear(); 453 | Parser parser; 454 | parser.load(buf, len); 455 | *this = parser.parse(); 456 | } 457 | 458 | Json::Json(Json &&other) noexcept { 459 | swap(other); 460 | } 461 | 462 | Json &Json::operator=(Json &&other) noexcept { 463 | swap(other); 464 | return *this; 465 | } 466 | 467 | void Json::swap(Json &other) { 468 | Type type = m_type; 469 | Value value = m_value; 470 | m_type = other.m_type; 471 | m_value = other.m_value; 472 | other.m_type = type; 473 | other.m_value = value; 474 | } 475 | 476 | void Json::append(Json &&value) { 477 | if (m_type != JSON_ARRAY) { 478 | clear(); 479 | m_type = JSON_ARRAY; 480 | m_value.m_array = new std::vector(); 481 | } 482 | m_value.m_array->push_back(std::move(value)); 483 | } 484 | -------------------------------------------------------------------------------- /src/jsonParser/Json.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using std::string; 8 | namespace breeze::json 9 | { 10 | class Json 11 | { 12 | public: 13 | enum Type 14 | { 15 | JSON_NULL = 0, 16 | JSON_BOOL, 17 | JSON_INT, 18 | JSON_DOUBLE, 19 | JSON_STRING, 20 | JSON_ARRAY, 21 | JSON_OBJECT 22 | }; 23 | Json(); 24 | Json(Type type); 25 | Json(bool value); 26 | Json(int value); 27 | Json(double value); 28 | Json(const char* value); 29 | Json(const string& value); 30 | Json(const Json& other); 31 | Json (Json && other) noexcept; 32 | ~Json(); 33 | 34 | Type type() const; 35 | bool is_null() const; 36 | bool is_bool() const; 37 | bool is_int() const; 38 | bool is_double() const; 39 | bool is_string() const; 40 | bool is_array() const; 41 | bool is_object() const; 42 | 43 | bool as_bool() const; 44 | int as_int() const; 45 | double as_double() const; 46 | string as_string() const; 47 | 48 | operator bool(); 49 | operator bool() const; 50 | operator int(); 51 | operator int() const; 52 | operator double(); 53 | operator double() const; 54 | operator string(); 55 | operator string()const; 56 | 57 | Json& operator = (bool value); 58 | Json& operator = (int value); 59 | Json& operator = (double value); 60 | Json& operator = (const char* value); 61 | Json& operator = (const string& value); 62 | Json& operator = (const Json& other); 63 | Json& operator = (Json&& other) noexcept; 64 | 65 | void append(const Json& value); 66 | void append(Json&& value); 67 | 68 | Json get(int index) const; 69 | Json get(const char* key) const; 70 | Json get(const string& key) const; 71 | 72 | void remove(int index); 73 | void remove(const char* key); 74 | void remove(const string& key); 75 | 76 | Json& operator [](int index); 77 | Json& operator [](const char* key); 78 | Json& operator [](const string& key); 79 | 80 | typedef std::vector::iterator iterator; 81 | 82 | iterator begin() 83 | { 84 | return m_value.m_array -> begin(); 85 | } 86 | iterator end() 87 | { 88 | return m_value.m_array -> end(); 89 | } 90 | 91 | string str()const; 92 | int size() const; 93 | bool empty() const; 94 | void parse(const string& file); 95 | void parse(const char* buf, int len); 96 | 97 | friend std::ostream& operator << (std::ostream& cout, const Json& json) 98 | { 99 | cout << json.str(); 100 | return cout; 101 | } 102 | 103 | void clear(); 104 | 105 | private: 106 | 107 | void copy(const Json& other); 108 | void swap(Json& other); 109 | bool has(int index) const; 110 | bool has(const char* key) const; 111 | bool has(const string& key) const; 112 | 113 | private: 114 | union Value// 共同体,内存占用取决于需要的最大内存 115 | { 116 | bool m_bool; 117 | int m_int; 118 | double m_double; 119 | string* m_string; 120 | std::vector* m_array; 121 | std::map* m_object; //全部弄成指针,节省内存 122 | }; 123 | Type m_type; 124 | Value m_value; 125 | }; 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/jsonParser/Parser.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser.h" 2 | #include 3 | #include 4 | using namespace breeze::json; 5 | 6 | void Parser::load(const string &fileName) { 7 | std::ifstream ifs(fileName); 8 | std::ostringstream oss; 9 | oss << ifs.rdbuf(); 10 | m_str = oss.str(); 11 | m_idx = 0; 12 | } 13 | 14 | void Parser::load(const char *buf, int len) { 15 | m_str.assign(buf, len); 16 | m_idx = 0; 17 | } 18 | 19 | Json Parser::parse() { 20 | char ch = get_next_token(); 21 | switch (ch) { 22 | case 'n': 23 | //null 24 | m_idx --; 25 | return parse_null(); 26 | case 't': 27 | case 'f': 28 | m_idx --; 29 | return parse_bool(); 30 | case '-': 31 | case '0': 32 | case '1': 33 | case '2': 34 | case '3': 35 | case '4': 36 | case '5': 37 | case '6': 38 | case '7': 39 | case '8': 40 | case '9': 41 | // number 42 | m_idx --; 43 | return parse_number(); 44 | case '"': 45 | //"string" 46 | return Json(parse_string()); 47 | case '[': 48 | //array 49 | return parse_array(); 50 | case '{': 51 | //object 52 | return parse_object(); 53 | default: 54 | break; 55 | } 56 | throw std::logic_error("unexpected character"); 57 | } 58 | 59 | void Parser::skip_white_space() { 60 | while(m_str[m_idx] == ' ' || m_str[m_idx] == '\r' || m_str[m_idx] == '\n' || m_str[m_idx] == '\t') 61 | { 62 | m_idx ++; 63 | } 64 | } 65 | 66 | char Parser::get_next_token() { 67 | skip_white_space(); 68 | if(m_idx == m_str.size()) 69 | { 70 | throw std::logic_error("ilegal operation"); 71 | } 72 | return m_str[m_idx ++]; 73 | } 74 | 75 | Json Parser::parse_null() { 76 | if(m_str.compare(m_idx, 4, "null") == 0) 77 | { 78 | m_idx += 4; 79 | return Json(); 80 | } 81 | 82 | throw std::logic_error("parse null error"); 83 | } 84 | 85 | Json Parser::parse_bool() { 86 | if(m_str.compare(m_idx, 4, "true") == 0) 87 | { 88 | m_idx += 4; 89 | return Json(true); 90 | } 91 | if(m_str.compare(m_idx, 5, "false") == 0) 92 | { 93 | m_idx += 5; 94 | return Json(false); 95 | } 96 | 97 | throw std::logic_error("parse bool error"); 98 | } 99 | 100 | Json Parser::parse_number() { 101 | size_t pos = m_idx; 102 | if (m_str[m_idx] == '-') 103 | { 104 | m_idx ++; 105 | } 106 | if(m_str[m_idx] == '0') 107 | { 108 | m_idx ++; 109 | } 110 | else if (in_range(m_str[m_idx], '1', '9')) 111 | { 112 | m_idx ++; 113 | while (in_range(m_str[m_idx], '0', '9')) 114 | { 115 | m_idx ++; 116 | } 117 | } 118 | else 119 | { 120 | throw std::logic_error("invalid character in number"); 121 | } 122 | if(m_str[m_idx] != '.') 123 | { 124 | return Json(std::atoi(m_str.c_str() + pos)); 125 | } 126 | m_idx ++; 127 | if(!in_range(m_str[m_idx], '0', '9')) 128 | { 129 | throw std::logic_error("invalid float number"); 130 | } 131 | while(in_range(m_str[m_idx], '0', '9')) 132 | { 133 | m_idx ++; 134 | } 135 | return Json(std::atof(m_str.c_str() + pos)); 136 | } 137 | 138 | Json Parser::parse_string() { 139 | int pos = m_idx; 140 | while(true) 141 | { 142 | char ch = get_next_token(); 143 | if (ch == '"') 144 | { 145 | break; 146 | } 147 | if (ch == '\\') 148 | { 149 | ch = get_next_token(); 150 | switch (ch) 151 | { 152 | case 'b': 153 | case 't': 154 | case 'n': 155 | case 'f': 156 | case 'r': 157 | case '"': 158 | case '\\': 159 | break; 160 | case 'u': 161 | m_idx += 4; 162 | break; 163 | default: 164 | break; 165 | } 166 | } 167 | } 168 | return Json(m_str.substr(pos, m_idx - pos - 1)); 169 | } 170 | 171 | Json Parser::parse_array() { 172 | Json arr(Json::JSON_ARRAY); 173 | char ch = get_next_token(); 174 | if (ch == ']') 175 | { 176 | return arr; 177 | } 178 | m_idx --; 179 | while(true) 180 | { 181 | arr.append(parse());//递归 182 | ch = get_next_token(); 183 | if (ch == ']') 184 | { 185 | break; 186 | } 187 | if(ch != ',') 188 | { 189 | throw std::logic_error("expected ',' in array"); 190 | } 191 | } 192 | return arr; 193 | } 194 | 195 | Json Parser::parse_object() { 196 | Json obj(Json::JSON_OBJECT); 197 | char ch = get_next_token(); 198 | if(ch == '}') 199 | { 200 | return obj; 201 | } 202 | m_idx --; 203 | while(true) 204 | { 205 | ch = get_next_token(); 206 | if(ch != '"') 207 | { 208 | throw std::logic_error("invalid object key"); 209 | } 210 | string key = parse_string(); 211 | ch = get_next_token(); 212 | if(ch != ':') 213 | { 214 | throw std::logic_error("expected ':' in object"); 215 | } 216 | obj[key] = parse(); 217 | ch = get_next_token(); 218 | if(ch == '}') 219 | { 220 | break; 221 | } 222 | if(ch != ',') 223 | { 224 | throw std::logic_error("expected ',' in object"); 225 | } 226 | } 227 | return obj; 228 | } 229 | 230 | bool Parser::in_range(int x, int lower, int upper) const 231 | { 232 | return x >= lower && x <= upper; 233 | } 234 | -------------------------------------------------------------------------------- /src/jsonParser/Parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Json.h" 4 | 5 | 6 | using std::string; 7 | namespace breeze::json 8 | { 9 | class Parser 10 | { 11 | public: 12 | Parser() = default; 13 | ~Parser() = default; 14 | 15 | void load(const string& fileName); 16 | void load(const char* buf, int len); 17 | Json parse(); 18 | private: 19 | void skip_white_space(); 20 | char get_next_token(); 21 | bool in_range(int x, int lower, int upper) const; 22 | Json parse_null(); 23 | Json parse_bool(); 24 | Json parse_number(); 25 | Json parse_string(); 26 | Json parse_array(); // 27 | Json parse_object();//用到了递归,值得细品 28 | private: 29 | string m_str; 30 | size_t m_idx = 0; 31 | }; 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | // #include "request.h" 3 | // #include "response.h" 4 | 5 | 6 | using namespace breeze::web; 7 | 8 | void post(const Request& req, Response& resp) 9 | { 10 | string name = req.post("name"); 11 | int age = req.post("age"); 12 | 13 | Json json; 14 | json["name"] = name; 15 | json["age"] = age; 16 | 17 | resp.json(json.str()); 18 | } 19 | 20 | 21 | int main() 22 | { 23 | auto server = Singleton::Instance(); 24 | 25 | //静态绑定 26 | server_handler post_handler = post; 27 | // server_handler index_handler = index; 28 | 29 | server -> bind("/post", post_handler); 30 | // server -> bind("/", index_handler); 31 | server -> start(); 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /src/reflect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(reflect 2 | DESCRIPTION "实现了C++中没有、而其他语言又很常见的反射机制" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | 7 | add_library(${PROJECT_NAME} ${DIR_SRC}) 8 | 9 | target_include_directories(${PROJECT_NAME} PUBLIC ${utility_Dir}) 10 | 11 | target_link_libraries(${PROJECT_NAME} PUBLIC utility) -------------------------------------------------------------------------------- /src/reflect/class_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "class_factory.h" 2 | 3 | using namespace breeze::reflect; 4 | 5 | void Object::set_class_name(const string & class_name) 6 | { 7 | m_class_name = class_name; 8 | } 9 | 10 | const string & Object::get_class_name() const 11 | { 12 | return m_class_name; 13 | } 14 | 15 | int Object::get_field_count() 16 | { 17 | return Singleton::Instance()->get_class_field_count(m_class_name); 18 | } 19 | 20 | ClassField * Object::get_field(int pos) 21 | { 22 | return Singleton::Instance()->get_class_field(m_class_name, pos); 23 | } 24 | 25 | ClassField * Object::get_field(const string & field_name) 26 | { 27 | return Singleton::Instance()->get_class_field(m_class_name, field_name); 28 | } 29 | 30 | void ClassFactory::register_class(const string & class_name, create_object func) 31 | { 32 | m_class_map[class_name] = func; 33 | } 34 | 35 | Object * ClassFactory::create_class(const string & class_name) 36 | { 37 | 38 | auto it = m_class_map.find(class_name); 39 | if (it == m_class_map.end()) 40 | { 41 | return nullptr; 42 | } 43 | return it->second(); 44 | } 45 | 46 | void ClassFactory::register_class_field(const string & class_name, const string & field_name, const string & field_type, size_t offset) 47 | { 48 | m_class_fields[class_name].push_back(new ClassField(field_name, field_type, offset)); 49 | } 50 | 51 | int ClassFactory::get_class_field_count(const string & class_name) 52 | { 53 | return (int)m_class_fields[class_name].size(); 54 | } 55 | 56 | ClassField * ClassFactory::get_class_field(const string & class_name, int pos) 57 | { 58 | int size = (int)m_class_fields[class_name].size(); 59 | if (pos < 0 || pos >= size) 60 | { 61 | return nullptr; 62 | } 63 | return m_class_fields[class_name][pos]; 64 | } 65 | 66 | ClassField * ClassFactory::get_class_field(const string & class_name, const string & field_name) 67 | { 68 | auto & fields = m_class_fields[class_name]; 69 | for (auto it = fields.begin(); it != fields.end(); ++it) 70 | { 71 | if ((*it)->name() == field_name) 72 | { 73 | return *it; 74 | } 75 | } 76 | return nullptr; 77 | } 78 | 79 | void ClassFactory::register_class_method(const string & class_name, const string &method_name, uintptr_t method) 80 | { 81 | m_class_methods[class_name].push_back(new ClassMethod(method_name, method)); 82 | } 83 | 84 | int ClassFactory::get_class_method_count(const string & class_name) 85 | { 86 | return (int)m_class_methods[class_name].size(); 87 | } 88 | 89 | ClassMethod * ClassFactory::get_class_method(const string & class_name, int pos) 90 | { 91 | int size = (int)m_class_methods[class_name].size(); 92 | if (pos < 0 || pos >= size) 93 | { 94 | return nullptr; 95 | } 96 | return m_class_methods[class_name][pos]; 97 | } 98 | 99 | ClassMethod * ClassFactory::get_class_method(const string & class_name, const string & method_name) 100 | { 101 | auto & methods = m_class_methods[class_name]; 102 | for (auto it = methods.begin(); it != methods.end(); ++it) 103 | { 104 | if ((*it)->name() == method_name) 105 | { 106 | return *it; 107 | } 108 | } 109 | return nullptr; 110 | } 111 | -------------------------------------------------------------------------------- /src/reflect/class_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "Singleton.h" 7 | using namespace breeze::utility; 8 | 9 | #include "class_field.h" 10 | #include "class_method.h" 11 | 12 | namespace breeze::reflect 13 | { 14 | 15 | class Object 16 | { 17 | public: 18 | Object() = default; 19 | virtual ~Object() = default; 20 | 21 | void set_class_name(const string & class_name); 22 | const string & get_class_name() const; 23 | 24 | int get_field_count(); 25 | ClassField * get_field(int pos); 26 | ClassField * get_field(const string & field_name); 27 | 28 | template 29 | void get(const string & field_name, T & value); 30 | 31 | template 32 | void set(const string & field_name, const T & value); 33 | 34 | template 35 | R call(const string & method_name, Args&&... args); 36 | 37 | private: 38 | string m_class_name; 39 | }; 40 | 41 | typedef Object * (*create_object)(); 42 | 43 | class ClassFactory 44 | { 45 | SINGLETON(ClassFactory); 46 | public: 47 | // reflect class 48 | void register_class(const string & class_name, create_object func); 49 | Object * create_class(const string & class_name); 50 | 51 | // reflect class field 52 | void register_class_field(const string & class_name, const string & field_name, const string & field_type, size_t offset); 53 | int get_class_field_count(const string & class_name); 54 | ClassField * get_class_field(const string & class_name, int pos); 55 | ClassField * get_class_field(const string & class_name, const string & field_name); 56 | 57 | // reflect class method 58 | void register_class_method(const string & class_name, const string &method_name, uintptr_t method); 59 | int get_class_method_count(const string & class_name); 60 | ClassMethod * get_class_method(const string & class_name, int pos); 61 | ClassMethod * get_class_method(const string & class_name, const string & method_name); 62 | 63 | bool map_is_empty() const 64 | { 65 | return m_class_map.empty(); 66 | } 67 | 68 | private: 69 | std::map m_class_map; 70 | std::map> m_class_fields; 71 | std::map> m_class_methods; 72 | }; 73 | 74 | template 75 | void Object::get(const string & field_name, T & value) 76 | { 77 | ClassField * field = Singleton::Instance()->get_class_field(m_class_name, field_name); 78 | if (field == nullptr) 79 | { 80 | return; 81 | } 82 | size_t offset = field->offset(); 83 | value = *((T *)((unsigned char *)(this) + offset)); 84 | } 85 | 86 | template 87 | void Object::set(const string & field_name, const T & value) 88 | { 89 | ClassField * field = Singleton::Instance()->get_class_field(m_class_name, field_name); 90 | if (field == nullptr) 91 | { 92 | return; 93 | } 94 | size_t offset = field->offset(); 95 | *((T *)((unsigned char *)(this) + offset)) = value; 96 | } 97 | 98 | template 99 | R Object::call(const string & method_name, Args&&... args) 100 | { 101 | ClassFactory * factory = Singleton::Instance(); 102 | ClassMethod * method = factory->get_class_method(m_class_name, method_name); 103 | if (method == nullptr) 104 | { 105 | return R(); 106 | } 107 | auto func = method->method(); 108 | typedef std::function class_method; 109 | return (*((class_method *)func))(this, args...); 110 | } 111 | } -------------------------------------------------------------------------------- /src/reflect/class_field.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using std::string; 5 | 6 | namespace breeze::reflect 7 | { 8 | class ClassField 9 | { 10 | public: 11 | ClassField() : m_name(""), m_type(""), m_offset(0) {} 12 | ClassField(const string & name, const string & type, size_t offset) : m_name(name), m_type(type), m_offset(offset) {} 13 | ~ClassField() = default; 14 | 15 | const string & name() const 16 | { 17 | return m_name; 18 | } 19 | 20 | const string & type() const 21 | { 22 | return m_type; 23 | } 24 | 25 | size_t offset() const 26 | { 27 | return m_offset; 28 | } 29 | 30 | private: 31 | string m_name; 32 | string m_type; 33 | size_t m_offset; 34 | }; 35 | 36 | } -------------------------------------------------------------------------------- /src/reflect/class_method.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | using std::string; 5 | 6 | namespace breeze::reflect 7 | { 8 | 9 | class ClassMethod 10 | { 11 | public: 12 | ClassMethod() : m_name(""), m_method(0) {} 13 | ClassMethod(const string & name, uintptr_t method) : m_name(name), m_method(method) {} 14 | ~ClassMethod() = default; 15 | 16 | const string & name() const 17 | { 18 | return m_name; 19 | } 20 | 21 | uintptr_t method() const 22 | { 23 | return m_method; 24 | } 25 | 26 | private: 27 | string m_name; 28 | uintptr_t m_method; 29 | }; 30 | 31 | } -------------------------------------------------------------------------------- /src/reflect/class_register.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "class_factory.h" 4 | 5 | namespace breeze::reflect 6 | { 7 | 8 | class ClassRegister 9 | { 10 | public: 11 | ClassRegister(const string & class_name, create_object func) 12 | { 13 | // register class 14 | Singleton::Instance()->register_class(class_name, func); 15 | } 16 | 17 | ClassRegister(const string & class_name, const string & field_name, const string & field_type, uintptr_t offset) 18 | { 19 | // register class field 20 | Singleton::Instance()->register_class_field(class_name, field_name, field_type, offset); 21 | } 22 | 23 | ClassRegister(const string & class_name, const string & method_name, uintptr_t method) 24 | { 25 | // register class method 26 | Singleton::Instance()->register_class_method(class_name, method_name, method); 27 | } 28 | }; 29 | 30 | 31 | #define REGISTER_CLASS(className) \ 32 | Object * createObject##className() \ 33 | { \ 34 | Object * obj = new className(); \ 35 | obj->set_class_name(#className); \ 36 | return obj; \ 37 | } \ 38 | ClassRegister classRegister##className(#className, createObject##className) 39 | 40 | #define REGISTER_CLASS_FIELD(className, fieldName, fieldType) \ 41 | className className##fieldName; \ 42 | ClassRegister classRegister##className##fieldName(#className, #fieldName, #fieldType, (size_t)(&(className##fieldName.fieldName)) - (size_t)(&className##fieldName)) 43 | 44 | #define REGISTER_CLASS_METHOD(className, methodName, returnType, ...) \ 45 | std::function className##methodName##method = &className::methodName; \ 46 | ClassRegister classRegister##className##methodName(#className, #methodName, (uintptr_t)&(className##methodName##method)) 47 | 48 | } -------------------------------------------------------------------------------- /src/socketHandler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(socketHandler 2 | DESCRIPTION "封装了socket,ServerSocket,ClientSocket,Epoll相关功能,最后封装SocketHandler作为外部调用的接口" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | 7 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 8 | 9 | target_include_directories(${PROJECT_NAME} PUBLIC 10 | ${ThreadPool_Dir} 11 | ${task_Dir} 12 | ) 13 | 14 | target_link_libraries(${PROJECT_NAME} PUBLIC task) 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/socketHandler/event_poller.cpp: -------------------------------------------------------------------------------- 1 | #include "event_poller.h" 2 | #include 3 | 4 | 5 | using namespace breeze::socket; 6 | 7 | EventPoller::EventPoller() : m_epfd(0), m_max_conn(0), m_events(nullptr) 8 | { 9 | 10 | } 11 | 12 | EventPoller::~EventPoller() 13 | { 14 | if (m_epfd > 0) 15 | { 16 | close(m_epfd); 17 | m_epfd = 0; 18 | } 19 | if (m_events != nullptr) 20 | { 21 | delete[] m_events; 22 | m_events = nullptr; 23 | } 24 | } 25 | 26 | bool EventPoller::create(int max_conn) 27 | { 28 | m_epfd = epoll_create(max_conn); 29 | if (m_epfd < 0) 30 | { 31 | return false; 32 | } 33 | m_events = new epoll_event[max_conn]; 34 | m_max_conn = max_conn; 35 | return true; 36 | } 37 | 38 | void EventPoller::add(int fd, u_int32_t events) 39 | { 40 | struct epoll_event ev; 41 | ev.events = events; 42 | ev.data.fd = fd; 43 | epoll_ctl(m_epfd, EPOLL_CTL_ADD, fd, &ev); 44 | } 45 | 46 | void EventPoller::mod(int fd, u_int32_t events) 47 | { 48 | struct epoll_event ev; 49 | ev.events = events; 50 | ev.data.fd = fd; 51 | epoll_ctl(m_epfd, EPOLL_CTL_MOD, fd, &ev); 52 | } 53 | 54 | void EventPoller::del(int fd) 55 | { 56 | epoll_ctl(m_epfd, EPOLL_CTL_DEL, fd, nullptr); 57 | } 58 | 59 | int EventPoller::wait(int timeout) 60 | { 61 | return epoll_wait(m_epfd, m_events, m_max_conn, timeout); 62 | } 63 | 64 | bool EventPoller::is_set(int idx, u_int32_t events) 65 | { 66 | return m_events[idx].events & events; 67 | } 68 | 69 | int EventPoller::get_fd(int idx) 70 | { 71 | return m_events[idx].data.fd; 72 | } -------------------------------------------------------------------------------- /src/socketHandler/event_poller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace breeze 6 | { 7 | namespace socket 8 | { 9 | class EventPoller 10 | { 11 | public: 12 | EventPoller(); 13 | ~EventPoller(); 14 | 15 | bool create(int max_conn); 16 | void add(int fd, u_int32_t events); 17 | void mod(int fd, u_int32_t events); 18 | void del(int fd); 19 | 20 | int wait(int timeout); // -1 无限等待, 0立即返回, >0 等待x milliseconds 21 | bool is_set(int idx, u_int32_t events); 22 | int get_fd(int idx); 23 | private: 24 | int m_epfd; //epoll 专用的文件描述符 25 | int m_max_conn; // epoll 的最大连接数 26 | struct epoll_event* m_events; //epoll 用于回传待处理事件的数组 27 | }; 28 | } 29 | } -------------------------------------------------------------------------------- /src/socketHandler/socket.cpp: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | 3 | using namespace breeze::socket; 4 | 5 | Socket::Socket() : m_ip(""), m_port(0), m_sockfd(0) 6 | { 7 | m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0); 8 | if(m_sockfd == -1) 9 | { 10 | log_error("create socket error: errno = %d errmsg = %s", errno, strerror(errno)); 11 | } 12 | log_debug("create socket success!"); 13 | } 14 | 15 | Socket::Socket(int sockfd) : m_ip(""), m_port(0), m_sockfd(sockfd){} 16 | 17 | 18 | int Socket::fd()const 19 | { 20 | return m_sockfd; 21 | } 22 | 23 | bool Socket::bind(const string &ip, int port) 24 | { 25 | struct sockaddr_in sockaddr; 26 | memset(&sockaddr, 0, sizeof(sockaddr)); 27 | sockaddr.sin_family = AF_INET; 28 | if(ip.empty()) 29 | { 30 | sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 31 | } 32 | else 33 | { 34 | inet_pton(AF_INET, ip.c_str(), &sockaddr.sin_addr.s_addr); 35 | } 36 | sockaddr.sin_port = htons(port); 37 | 38 | if(::bind(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == -1) 39 | { 40 | log_error("socket bind error: errno = %d, errmsg = %s", errno, strerror(errno)); 41 | return false; 42 | } 43 | m_ip = ip; 44 | m_port = port; 45 | log_debug("socket bind success: ip = %s, port = %d", ip.c_str(), port); 46 | return true; 47 | } 48 | 49 | bool Socket::listen(int backlog) 50 | { 51 | if(::listen(m_sockfd, backlog) == -1) 52 | { 53 | log_error("socket listen errro, errno = %d, errmsg = %s", errno, strerror(errno)); 54 | return false; 55 | } 56 | log_debug("socket listening ..."); 57 | return true; 58 | } 59 | 60 | bool Socket::connect(const string &ip, int port) 61 | { 62 | struct sockaddr_in sockaddr; 63 | memset(&sockaddr, 0, sizeof(sockaddr)); 64 | sockaddr.sin_family = AF_INET; 65 | sockaddr.sin_port = htons(port); 66 | inet_pton(AF_INET, ip.c_str(), &sockaddr.sin_addr.s_addr); 67 | 68 | if(::connect(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) 69 | { 70 | log_error("socket connect error: errno = %d, errmsg = %s", errno, strerror(errno)); 71 | return false; 72 | } 73 | m_ip = ip; 74 | m_port = port; 75 | return true; 76 | } 77 | 78 | int Socket::accept() 79 | { 80 | int connfd = ::accept(m_sockfd, nullptr, nullptr); 81 | if(connfd < 0) 82 | { 83 | log_error("socket accept error: errno = %d, errmsg = %s", errno, strerror(errno)); 84 | return -1; 85 | } 86 | log_debug("socket accept success, connfd = %d", connfd); 87 | return connfd; 88 | } 89 | 90 | int Socket::send(const char *buf, int len) 91 | { 92 | return ::send(m_sockfd, buf, len, 0); 93 | } 94 | 95 | int Socket::recv(char *buf, int len) 96 | { 97 | return ::recv(m_sockfd, buf, len, 0); 98 | } 99 | 100 | void Socket::close() 101 | { 102 | if(m_sockfd > 0) 103 | { 104 | ::close(m_sockfd); 105 | m_sockfd = 0; 106 | } 107 | } 108 | 109 | //一以下内容用来修改socket的设置 110 | 111 | bool Socket::set_non_blocking() 112 | { 113 | int flags = fcntl(m_sockfd, F_GETFL, 0); 114 | if (flags < 0) 115 | { 116 | log_error("socket set_non_blocking error: errno = %d, errmsg = %s", errno, strerror(errno)); 117 | return false; 118 | } 119 | flags |= O_NONBLOCK; 120 | if(fcntl(m_sockfd, F_SETFL, flags) < 0) 121 | { 122 | log_error("socket set_non_blocking error: errno = %d, errmsg = %s", errno, strerror(errno)); 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | bool Socket::set_send_buffer(int size) 130 | { 131 | int buff_size = size; 132 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, &buff_size, sizeof(buff_size)) < 0) 133 | { 134 | log_error("socket set_send_buffer error: errno = %d, errmsg = %s", errno, strerror(errno)); 135 | return false; 136 | } 137 | return true; 138 | } 139 | 140 | bool Socket::set_recv_buffer(int size) 141 | { 142 | int buff_size = size; 143 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_RCVBUF, &buff_size, sizeof(buff_size))) 144 | { 145 | log_error("socket set_send_buffer error: errno = %d, errmsg = %s", errno, strerror(errno)); 146 | return false; 147 | } 148 | return true; 149 | } 150 | 151 | bool Socket::set_linger(bool active, int seconds) 152 | { 153 | struct linger l; 154 | memset(&l, 0, sizeof(l)); 155 | l.l_onoff = active ? 1 : 0; 156 | l.l_linger = seconds; 157 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) 158 | { 159 | log_error("socket set_linger error, errno = %d, errmsg = %s", errno, strerror(errno)); 160 | return false; 161 | } 162 | return true; 163 | } 164 | 165 | bool Socket::set_keepalive() 166 | { 167 | int flag = 1; // true or false 168 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0) 169 | { 170 | log_error("socket set_keepalive error, errno = %d, errmsg = %s", errno, strerror(errno)); 171 | return false; 172 | } 173 | return true; 174 | } 175 | 176 | bool Socket::set_reuse_addr() 177 | { 178 | int flag = 1; 179 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) 180 | { 181 | log_error("socket set_keepalive error, errno = %d, errmsg = %s", errno, strerror(errno)); 182 | return false; 183 | } 184 | return true; 185 | } 186 | 187 | 188 | Client_socket::Client_socket(const string& ip, int port) : Socket() 189 | { 190 | connect(ip, port); 191 | } 192 | 193 | Serversocket::Serversocket(const string& ip, int port):Socket() 194 | { 195 | set_non_blocking(); 196 | set_recv_buffer(10 * 1024); 197 | set_send_buffer(10 * 1024); 198 | set_linger(true, 0); 199 | set_keepalive(); 200 | set_reuse_addr(); 201 | 202 | //TODO:这里可以补一点逻辑 203 | bind(ip, port); 204 | listen(1024); 205 | } -------------------------------------------------------------------------------- /src/socketHandler/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Logger.h" 10 | 11 | using namespace breeze::utility; 12 | using std::string; 13 | 14 | namespace breeze::socket 15 | { 16 | 17 | class Socket 18 | { 19 | public: 20 | Socket(); 21 | Socket(int sockfd); 22 | virtual ~Socket() = default; 23 | 24 | int fd()const; 25 | bool bind(const string& ip, int port); 26 | bool listen(int backlog); 27 | bool connect(const string& ip, int port); 28 | int accept(); 29 | int send(const char* buf, int len); 30 | int recv(char* buf, int len); 31 | void close(); 32 | 33 | bool set_non_blocking(); 34 | bool set_send_buffer(int size); 35 | bool set_recv_buffer(int size); 36 | bool set_linger(bool active, int seconds); 37 | bool set_keepalive(); 38 | bool set_reuse_addr(); 39 | protected: 40 | string m_ip; 41 | int m_port; 42 | int m_sockfd; 43 | }; 44 | 45 | class Client_socket : public Socket 46 | { 47 | public: 48 | Client_socket() = delete; 49 | Client_socket(const string& ip, int port); 50 | ~Client_socket() = default; 51 | }; 52 | 53 | class Serversocket : public Socket 54 | { 55 | public: 56 | Serversocket() = delete; 57 | Serversocket(const string& ip, int port); 58 | ~Serversocket() = default; 59 | }; 60 | } -------------------------------------------------------------------------------- /src/socketHandler/socket_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "socket_handler.h" 2 | #include "Logger.h" 3 | #include "Singleton.h" 4 | #include "socket.h" 5 | #include "task_dispatcher.h" 6 | #include 7 | #include "task_factory.h" 8 | 9 | 10 | using namespace breeze::thread; 11 | using namespace breeze::socket; 12 | using namespace breeze::task; 13 | 14 | void SocketHandler::listen(const string& ip, int port) 15 | { 16 | m_server = new Serversocket(ip, port); 17 | } 18 | void SocketHandler::attach(int sockfd) 19 | { 20 | m_epoll.add(sockfd, EPOLLIN|EPOLLHUP|EPOLLERR|EPOLLONESHOT); 21 | } 22 | void SocketHandler::detach(int sockfd) 23 | { 24 | m_epoll.del(sockfd); 25 | } 26 | 27 | void SocketHandler::handle(int max_conn, int timeout) 28 | { 29 | m_epoll.create(max_conn); 30 | u_int32_t events = EPOLLIN|EPOLLHUP|EPOLLERR; 31 | m_epoll.add(m_server -> fd(), events); 32 | 33 | while (true) 34 | { 35 | int num = m_epoll.wait(timeout); 36 | if (num < 0) { 37 | if (errno == EINTR) 38 | { 39 | // epoll_wait 被信号中断,继续重试 40 | continue; 41 | } 42 | else 43 | { 44 | log_error("epoll wait error: errno = %d, errmsg = %s", errno, strerror(errno)); 45 | break; 46 | } 47 | } 48 | else if (num == 0) 49 | { 50 | continue; 51 | } 52 | for (int i = 0; i < num; ++ i) 53 | { 54 | if (m_epoll.get_fd(i) == m_server -> fd()) 55 | { 56 | //服务端套接字可读 57 | int connfd = m_server -> accept(); 58 | 59 | //将连接套接字添加到监听队列 60 | struct epoll_event ev2; 61 | Socket client(connfd); 62 | client.set_non_blocking(); 63 | attach(connfd); 64 | } 65 | else 66 | { 67 | int connfd = m_epoll.get_fd(i); 68 | if (m_epoll.is_set(i, EPOLLHUP)) // 客户端异常退出会被epoll检测为EPOLLHUP 69 | { 70 | //连接套接字挂断了 71 | log_error("socket hang up by peer: errno = %d, errmsg = %s", errno, strerror(errno)); 72 | detach(connfd); 73 | ::close(connfd); 74 | } 75 | else if (m_epoll.is_set(i, EPOLLERR)) 76 | { 77 | //连接套接字出现错误 78 | log_error("socket error: connfd = %d, errno = %d, errmsg = %s", connfd, errno, strerror(errno)); 79 | detach(connfd); 80 | ::close(connfd); 81 | 82 | } 83 | else if (m_epoll.is_set(i, EPOLLIN)) 84 | { 85 | detach(connfd); 86 | 87 | auto task = Singleton::Instance() -> create(connfd); 88 | 89 | Singleton::Instance() -> assign(std::move(task)); 90 | } 91 | 92 | 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /src/socketHandler/socket_handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Singleton.h" 3 | #include "event_poller.h" 4 | #include "socket.h" 5 | 6 | 7 | 8 | using namespace breeze::utility; 9 | 10 | namespace breeze 11 | { 12 | namespace socket 13 | { 14 | class SocketHandler 15 | { 16 | SINGLETON(SocketHandler); 17 | 18 | public: 19 | void listen(const string& ip, int port); 20 | void attach(int sockfd); 21 | void detach(int sockfd); 22 | void handle(int max_conn, int timeout); 23 | 24 | private: 25 | Socket* m_server = nullptr; 26 | EventPoller m_epoll; 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/thread/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(ThreadPool 2 | DESCRIPTION "使用C++11封装的一个线程池" 3 | ) 4 | 5 | aux_source_directory(. DIR_SRC) 6 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 7 | target_include_directories(${PROJECT_NAME} PUBLIC ${utility_Dir}) -------------------------------------------------------------------------------- /src/thread/task.cpp: -------------------------------------------------------------------------------- 1 | #include "task.h" 2 | using namespace breeze::thread; 3 | 4 | Task::Task() : m_data(nullptr) 5 | { 6 | } 7 | 8 | Task::Task(void * data) : m_data(data) 9 | { 10 | } 11 | 12 | Task::~Task() 13 | { 14 | } 15 | 16 | void * Task::get_data() 17 | { 18 | return m_data; 19 | } 20 | 21 | void Task::set_data(void * data) 22 | { 23 | m_data = data; 24 | } 25 | -------------------------------------------------------------------------------- /src/thread/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace breeze 4 | { 5 | namespace thread 6 | { 7 | class Task 8 | { 9 | public: 10 | Task(); 11 | Task(void* data); 12 | virtual ~Task(); 13 | 14 | void * get_data(); 15 | void set_data(void * data); 16 | 17 | virtual void run() = 0; 18 | virtual void destroy() = 0; 19 | 20 | protected: 21 | void * m_data; 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/thread/task_dispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "task_dispatcher.h" 2 | #include "thread_pool.h" 3 | #include "Logger.h" 4 | #include 5 | using namespace breeze::thread; 6 | 7 | void TaskDispatcher::init(int threads) 8 | { 9 | Singleton::Instance()->create(threads); 10 | start(); 11 | } 12 | 13 | void TaskDispatcher::assign(std::shared_ptr&& task) 14 | { 15 | log_debug("task dispatcher receive and ready to assign task: %x", task.get()); 16 | { 17 | std::unique_lock lck(m_mutex); 18 | log_debug("task : %x, use_count = %d", task.get(), task.use_count()); 19 | 20 | m_queue.push_back(std::move(task)); 21 | 22 | log_debug("before notify, m_queue.size = %d", m_queue.size()); 23 | } 24 | m_cond.notify_one(); 25 | 26 | log_debug("task dispatcher push a task into queue"); 27 | } 28 | 29 | void TaskDispatcher::handle(std::shared_ptr&& task) 30 | { 31 | auto pool = Singleton::Instance(); 32 | if (!pool->empty()) 33 | { 34 | log_debug("task allocated to threadpool : %x", task.get()); 35 | pool->assign(std::move(task)); 36 | log_debug("back from pool assign"); 37 | } 38 | else 39 | { 40 | std::unique_lock lck(m_mutex); 41 | log_warn("all threads are busy!"); 42 | m_queue.push_front(std::move(task)); 43 | } 44 | } 45 | 46 | bool TaskDispatcher::empty() 47 | { 48 | std::unique_lock lck(m_mutex); 49 | return m_queue.empty(); 50 | } 51 | 52 | void TaskDispatcher::run() 53 | { 54 | log_debug("taskdispatcher thread id = %x", std::this_thread::get_id()); 55 | while (true) 56 | { 57 | std::unique_lock lck(m_mutex); 58 | while (m_queue.empty()) 59 | { 60 | m_cond.wait(lck); 61 | log_debug("after wake up, m_queue.size = %d", m_queue.size()); 62 | } 63 | 64 | log_debug("function run receive a signal "); 65 | auto task = std::move(m_queue.front()); 66 | log_debug("TaskDispatcher take a task from queue: %x", task.get()); 67 | 68 | m_queue.pop_front(); 69 | lck.unlock(); 70 | 71 | log_debug("ready to handle task: %x", task.get()); 72 | handle(std::move(task)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/thread/task_dispatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "thread.h" 5 | #include "Singleton.h" 6 | #include 7 | #include 8 | #include 9 | #include "task.h" 10 | 11 | using namespace breeze::utility; 12 | 13 | namespace breeze 14 | { 15 | namespace thread 16 | { 17 | class TaskDispatcher : public Thread 18 | { 19 | SINGLETON(TaskDispatcher); 20 | public: 21 | void init(int threads); 22 | void assign(std::shared_ptr&& task); 23 | void handle(std::shared_ptr&& task); 24 | bool empty(); 25 | virtual void run(); 26 | 27 | protected: 28 | std::list> m_queue; 29 | std::mutex m_mutex; 30 | std::condition_variable m_cond; 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/thread/thread.cpp: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | using namespace breeze::thread; 3 | 4 | Thread::Thread() 5 | { 6 | } 7 | 8 | Thread::~Thread() 9 | { 10 | } 11 | 12 | void Thread::start() 13 | { 14 | m_thread = std::thread(Thread::thread_func, this); 15 | m_thread.detach(); 16 | } 17 | 18 | void Thread::stop() 19 | { 20 | } 21 | 22 | void * Thread::thread_func(void * ptr) 23 | { 24 | auto thread = (Thread *)ptr; 25 | thread->run(); 26 | return ptr; 27 | } 28 | -------------------------------------------------------------------------------- /src/thread/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace breeze 6 | { 7 | namespace thread 8 | { 9 | class Thread //虚基类 10 | { 11 | public: 12 | Thread(); 13 | virtual ~Thread(); 14 | 15 | virtual void run() = 0; 16 | 17 | void start(); 18 | void stop(); 19 | 20 | protected: 21 | static void * thread_func(void * ptr); 22 | 23 | protected: 24 | std::thread m_thread; 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/thread/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "thread_pool.h" 2 | #include 3 | using namespace breeze::thread; 4 | 5 | void ThreadPool::create(int threads) 6 | { 7 | std::unique_lock lck(m_mutex); 8 | m_threads = threads; 9 | for (int i = 0; i < threads; i++) 10 | { 11 | auto thread = new WorkerThread(); 12 | m_pool.push_back(thread); 13 | thread->start(); 14 | } 15 | log_debug("thread pool create worker threads: %d", threads); 16 | } 17 | 18 | WorkerThread * ThreadPool::get() 19 | { 20 | std::unique_lock lck(m_mutex); 21 | while (m_pool.empty()) 22 | m_cond.wait(lck); 23 | auto thread = m_pool.front(); 24 | m_pool.pop_front(); 25 | return thread; 26 | } 27 | 28 | void ThreadPool::put(WorkerThread * thread) 29 | { 30 | std::unique_lock lck(m_mutex); 31 | m_pool.push_back(thread); 32 | m_cond.notify_one(); 33 | } 34 | 35 | bool ThreadPool::empty() 36 | { 37 | std::unique_lock lck(m_mutex); 38 | return m_pool.empty(); 39 | } 40 | 41 | void ThreadPool::assign(std::shared_ptr task) 42 | { 43 | auto thread = get(); 44 | thread->assign(task); 45 | } 46 | -------------------------------------------------------------------------------- /src/thread/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "worker_thread.h" 4 | #include "Singleton.h" 5 | 6 | using namespace breeze::utility; 7 | 8 | namespace breeze 9 | { 10 | namespace thread 11 | { 12 | class ThreadPool 13 | { 14 | SINGLETON(ThreadPool); 15 | public: 16 | void create(int threads); 17 | void assign(std::shared_ptr task); 18 | 19 | WorkerThread * get(); 20 | void put(WorkerThread * thread); 21 | bool empty(); 22 | 23 | private: 24 | int m_threads = 0; 25 | std::list m_pool; 26 | std::mutex m_mutex; 27 | std::condition_variable m_cond; 28 | }; 29 | 30 | }} 31 | -------------------------------------------------------------------------------- /src/thread/worker_thread.cpp: -------------------------------------------------------------------------------- 1 | #include "worker_thread.h" 2 | #include "Logger.h" 3 | #include "thread_pool.h" 4 | 5 | 6 | using namespace breeze::utility; 7 | using namespace breeze::thread; 8 | 9 | WorkerThread::WorkerThread() : Thread(), m_task(nullptr) 10 | { 11 | } 12 | 13 | WorkerThread::~WorkerThread() 14 | { 15 | } 16 | 17 | void WorkerThread::run() 18 | { 19 | while (true) 20 | { 21 | std::unique_lock lck(m_mutex); 22 | 23 | while (m_task == nullptr) 24 | m_cond.wait(lck); 25 | 26 | lck.unlock(); 27 | 28 | log_debug("worker thread run: thread=%x, task=%x", this, m_task.get()); 29 | 30 | m_task-> run(); 31 | m_task-> destroy(); 32 | m_task = nullptr; 33 | 34 | Singleton::Instance()->put(this); 35 | } 36 | } 37 | 38 | void WorkerThread::assign(std::shared_ptr task) 39 | { 40 | std::unique_lock lck(m_mutex); 41 | m_task = task; 42 | m_cond.notify_one(); 43 | } 44 | -------------------------------------------------------------------------------- /src/thread/worker_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "thread.h" 7 | #include "task.h" 8 | 9 | namespace breeze 10 | { 11 | namespace thread 12 | { 13 | class WorkerThread : public Thread 14 | { 15 | public: 16 | WorkerThread(); 17 | ~WorkerThread(); 18 | 19 | virtual void run(); 20 | void assign(std::shared_ptr task); 21 | 22 | private: 23 | std::shared_ptr m_task; 24 | std::mutex m_mutex; 25 | std::condition_variable m_cond; 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/utility/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(utility) 2 | 3 | aux_source_directory(. DIR_SRC) 4 | 5 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 6 | set_target_properties(${PROJECT_NAME} PROPERTIES 7 | POSITION_INDEPENDENT_CODE ON 8 | ) -------------------------------------------------------------------------------- /src/utility/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace breeze::utility; 9 | 10 | const char* Logger::s_level[LOG_COUNT] = 11 | { 12 | "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 13 | }; 14 | 15 | 16 | void Logger::open(const std::string &fileName) 17 | { 18 | m_fileName = fileName; 19 | 20 | if (m_ofs.is_open()) 21 | { 22 | m_ofs.close(); 23 | } 24 | 25 | m_ofs.open(fileName, std::ios::app); 26 | if(m_ofs.fail()) 27 | { 28 | std::cout << "errmsg = " << strerror(errno) << std::endl; 29 | throw std::runtime_error("open log file failed: " + fileName); 30 | } 31 | //这里考虑翻滚的时候要重置长度, 以下写法比重置为0更准确 32 | m_ofs.seekp(0, std::ios::end); 33 | m_len = (int)m_ofs.tellp(); 34 | } 35 | 36 | void Logger::close() 37 | { 38 | m_ofs.close(); 39 | } 40 | 41 | void Logger::localtime(struct tm* time_info, const time_t* ticks) 42 | { 43 | #ifdef WIN32 44 | localtime_s(time_info, ticks); 45 | #else 46 | localtime_r(ticks, time_info); 47 | #endif 48 | } 49 | 50 | void Logger::sleep(int millseconds) 51 | { 52 | #ifdef WIN32 53 | Sleep(millseconds); 54 | #else 55 | usleep(millseconds*1000); 56 | #endif 57 | } 58 | 59 | void Logger::log(Level level, const char* file, int line, const char* format, ...) 60 | { 61 | //忽略低级别的日志,保留高级别的日志 62 | if(m_level > level) 63 | { 64 | return; 65 | } 66 | if(m_ofs.fail()) 67 | { 68 | return; 69 | } 70 | std::ostringstream oss; 71 | 72 | time_t ticks = time(nullptr); // 获取秒数 73 | tm time_info = {0}; //时间结构体,将各个属性初始化为0 74 | localtime(&time_info, &ticks); //将系统时间输入time_info 75 | //转化为特定格式存储 76 | char timestamp[32] = {0}; 77 | strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &time_info); 78 | 79 | int len = 0; 80 | const char* formater = "%s %s %s:%d "; 81 | 82 | //第一次计算长度,前两个参数为特殊用法 83 | len = snprintf(nullptr, 0, formater, timestamp, s_level[level], file, line); 84 | 85 | if(len > 0) 86 | { 87 | char* buffer = new char[len + 1]; //包括'\0' 88 | //实际写入 89 | snprintf(buffer, len + 1, formater, timestamp, s_level[level], file, line); 90 | buffer[len] = 0;//最后一位设为'\0' 91 | oss << buffer; 92 | m_len += len; 93 | delete[] buffer; 94 | } 95 | 96 | //以上内容处理,内容前的东西 97 | 98 | va_list arg_ptr; //处理可变参数... 99 | va_start(arg_ptr, format); //将arg_ptr 指向第一个可变参数 100 | len = vsnprintf(nullptr, 0, format, arg_ptr); //用法类似于snprintf 101 | va_end(arg_ptr); //清理va_list 102 | if(len > 0) 103 | { 104 | //如果长度大于零,则继续操作 105 | char* content = new char[len + 1]; 106 | va_start(arg_ptr, format); 107 | vsnprintf(content, len + 1, format, arg_ptr);//实际将内容写入content 108 | va_end(arg_ptr); 109 | content[len] = 0; 110 | oss << content; 111 | m_len += len; 112 | delete[] content; 113 | } 114 | oss << "\n"; 115 | 116 | const string& str = oss.str(); 117 | 118 | std::unique_lock lck(m_mutex); 119 | 120 | m_ofs << str; 121 | m_ofs.flush(); 122 | 123 | if(m_console) 124 | { 125 | std::cout << str; 126 | } 127 | 128 | if(m_maxSize > 0 && m_len >= m_maxSize) 129 | { 130 | //日志需要翻滚 131 | rotate(); 132 | } 133 | } 134 | 135 | void Logger::setLevel(Level level) 136 | { 137 | m_level = level; 138 | } 139 | 140 | void Logger::setMaxSize(int maxSize) 141 | { 142 | m_maxSize = maxSize; 143 | } 144 | 145 | void Logger::rotate() 146 | { 147 | close(); 148 | sleep(1000); 149 | time_t ticks = time(nullptr); 150 | struct tm time_info = {0}; 151 | localtime(&time_info, &ticks); 152 | char timestamp[32] = {0}; 153 | strftime(timestamp, sizeof timestamp, ".%Y-%m-%d_%H-%M-%S", &time_info); 154 | 155 | string fileName = m_fileName + timestamp; 156 | //把写好的备份,再重新打开 157 | if(rename(m_fileName.c_str(), fileName.c_str()) != 0) 158 | { 159 | std::cout << m_fileName << " " << fileName << std::endl; 160 | throw std::logic_error("rename fileName failed"); 161 | } 162 | open(m_fileName); 163 | } 164 | 165 | void Logger::setConsole(bool console) 166 | { 167 | m_console = console; 168 | } 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /src/utility/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Singleton.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #ifdef WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | using std::string; 16 | 17 | namespace breeze 18 | { 19 | namespace utility 20 | { 21 | #define log_debug(format, ...) \ 22 | Singleton::Instance() -> log(Logger::LOG_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__) 23 | #define log_info(format, ...) \ 24 | Singleton::Instance() -> log(Logger::LOG_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__) 25 | #define log_warn(format, ...) \ 26 | Singleton::Instance() -> log(Logger::LOG_WARN, __FILE__, __LINE__, format, ##__VA_ARGS__) 27 | #define log_error(format, ...) \ 28 | Singleton::Instance() -> log(Logger::LOG_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__) 29 | #define log_fatal(format, ...) \ 30 | Singleton::Instance() -> log(Logger::LOG_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__) 31 | class Logger 32 | { 33 | SINGLETON(Logger); 34 | public : 35 | ~Logger() = default; 36 | enum Level 37 | { 38 | LOG_DEBUG = 0, 39 | LOG_INFO, 40 | LOG_WARN, 41 | LOG_ERROR, 42 | LOG_FATAL, 43 | LOG_COUNT 44 | }; 45 | 46 | 47 | 48 | void open(const string& fileName); 49 | void close(); 50 | void log(Level level, const char* file, int line, const char* format, ...);//日志写入函数 51 | void setLevel(Level level); 52 | void setMaxSize(int maxSize); 53 | void setConsole(bool console); 54 | 55 | private: 56 | void rotate(); 57 | void localtime(struct tm* time_info, const time_t* ticks); 58 | void sleep(int millseconds); //跨平台的localtime 和 sleep 函数 59 | private: 60 | string m_fileName; 61 | std::ofstream m_ofs; 62 | int m_maxSize = 0; 63 | int m_level = LOG_DEBUG; 64 | int m_len = 0; 65 | int m_console = true; 66 | static const char* s_level[LOG_COUNT]; 67 | std::mutex m_mutex; 68 | }; 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/utility/Singleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace breeze 4 | { 5 | namespace utility 6 | { 7 | //单例模式模板类 8 | template 9 | class Singleton 10 | { 11 | public: 12 | //线程安全,类似于饿汉式 13 | static T* Instance() 14 | { 15 | static T instance; 16 | return &instance; 17 | } 18 | private: 19 | Singleton() = default; 20 | Singleton(const Singleton&) = delete; 21 | Singleton& operator = (const Singleton &) = delete; 22 | ~Singleton() = default; 23 | }; 24 | #define SINGLETON(classname) \ 25 | friend class Singleton; \ 26 | private: \ 27 | classname() = default; \ 28 | classname(const classname &) = delete; \ 29 | classname & operator = (const classname &) = delete; 30 | } 31 | } -------------------------------------------------------------------------------- /src/utility/String.cpp: -------------------------------------------------------------------------------- 1 | #include "String.h" 2 | #include 3 | #include 4 | using namespace breeze::utility; 5 | 6 | string String::to_lower(const std::string &input) 7 | { 8 | string str = input; 9 | //单独的::意思是全局 10 | std::transform(str.begin(), str.end(), str.begin(), ::tolower); 11 | return str; 12 | } 13 | 14 | string String::to_upper(const std::string &input) 15 | { 16 | string str = input; 17 | std::transform(str.begin(), str.end(), str.begin(), ::toupper); 18 | return str; 19 | } 20 | 21 | string String::ltrim(const string &input, const string &trims) 22 | { 23 | string str = input; 24 | size_t pos = str.find_first_not_of(trims); 25 | if(pos != string::npos) 26 | { 27 | str.erase(0, pos); 28 | } 29 | else 30 | { 31 | str.clear(); 32 | } 33 | return str; 34 | } 35 | 36 | string String::rtrim(const string &input, const string &trims) 37 | { 38 | string str = input; 39 | size_t pos = str.find_last_not_of(trims); 40 | if(pos != string::npos) 41 | { 42 | str.erase(pos + 1); 43 | } 44 | else 45 | { 46 | str.clear(); 47 | } 48 | return str; 49 | } 50 | 51 | string String::trim(const string &input, const string &trims) 52 | { 53 | string str = ltrim(input, trims); 54 | str = rtrim(str, trims); 55 | return str; 56 | } 57 | 58 | std::vector String::split(const string &input, const string &separator) 59 | { 60 | std::vector Ret; 61 | if (separator.empty() || input.empty()) 62 | { 63 | return Ret; 64 | } 65 | size_t last = 0; 66 | size_t index = input.find_first_of(separator, last); 67 | 68 | while(index != string::npos) 69 | { 70 | if (index != last) 71 | { 72 | std::string str = input.substr(last, index - last); 73 | Ret.push_back(str); 74 | } 75 | last = index + 1; 76 | index = input.find_first_of(separator, last); 77 | } 78 | if(index - last > 0) 79 | { 80 | Ret.push_back(input.substr(last)); 81 | } 82 | return Ret; 83 | } 84 | 85 | std::vector String::split(const string &input, char separator) 86 | { 87 | return split(input, string(1, separator)); 88 | } 89 | 90 | string String::join(const std::vector &input, const string &joiner) 91 | { 92 | std::ostringstream oss; 93 | for(auto it = input.begin(); it != input.end(); ++ it) 94 | { 95 | if(it != input.begin()) 96 | { 97 | oss << joiner; 98 | } 99 | oss << *it; 100 | } 101 | return oss.str(); 102 | } 103 | 104 | string String::join(const std::vector &input, char joiner) 105 | { 106 | return join(input, string(1, joiner)); 107 | } 108 | 109 | bool String::hasPrefix(const string &input, const string &prefix) 110 | { 111 | if(input.size() < prefix.size()) return false; 112 | return input.substr(0, prefix.size()) == prefix; 113 | } 114 | 115 | bool String::hasSuffix(const string &input, const string &suffix) 116 | { 117 | if(input.size() < suffix.size()) return false; 118 | return input.substr(input.size() - suffix.size()) == suffix; 119 | } 120 | 121 | string String::format(const char* formater, ...) 122 | { 123 | string Ret; 124 | va_list arg_ptr; 125 | va_start(arg_ptr, formater); 126 | int len = vsnprintf(nullptr, 0, formater, arg_ptr); 127 | va_end(arg_ptr); 128 | 129 | if(len > 0) 130 | { 131 | char* buf = new char[len + 1]; 132 | va_start(arg_ptr, formater); 133 | vsnprintf(buf, len + 1, formater, arg_ptr); 134 | va_end(arg_ptr); 135 | buf[len] = 0; 136 | Ret = buf; 137 | delete[] buf; 138 | } 139 | return Ret; 140 | } 141 | 142 | string String::capitalize(const string &input) 143 | { 144 | string str = input; 145 | if(str.empty()) 146 | { 147 | return str; 148 | } 149 | char ch = str[0]; 150 | if(islower(ch)) 151 | { 152 | ch = (char)toupper(ch); 153 | std::replace(str.begin(), str.begin() + 1, str[0], ch); 154 | } 155 | return str; 156 | } 157 | -------------------------------------------------------------------------------- /src/utility/String.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_STRING_H 2 | #define STRING_STRING_H 3 | #include 4 | #include 5 | #include 6 | using std::string; 7 | 8 | namespace breeze::utility { 9 | 10 | 11 | class String 12 | { 13 | public: 14 | String() = default; 15 | ~String() = default; 16 | 17 | static string to_lower(const string& input); 18 | static string to_upper(const string& input); 19 | 20 | static string ltrim(const string& input, const string& trims); 21 | static string rtrim(const string& input, const string& trims); 22 | static string trim(const string& input, const string& trims); 23 | 24 | static std::vector split(const string& input, const string& separator); 25 | static std::vector split(const string& input, char separator); 26 | 27 | static string join(const std::vector& input, const string& joiner); 28 | static string join(const std::vector& input, char joiner); 29 | 30 | static bool hasPrefix(const string& input, const string& prefix); 31 | static bool hasSuffix(const string& input, const string& suffix); 32 | 33 | static string capitalize(const string& input); 34 | static string format(const char* input, ...); 35 | }; 36 | 37 | 38 | } 39 | 40 | #endif //STRING_STRING_H 41 | -------------------------------------------------------------------------------- /src/utility/Value.cpp: -------------------------------------------------------------------------------- 1 | #include "Value.h" 2 | #include 3 | #include 4 | 5 | using namespace breeze::utility; 6 | 7 | Value::Value():type(V_NULL) 8 | { 9 | 10 | } 11 | 12 | Value::Value(bool value_):type(V_BOOL) 13 | { 14 | value = value_ ? "true":"false"; 15 | } 16 | Value::Value(int value_):type(V_INT) 17 | { 18 | value = std::to_string(value_); 19 | } 20 | Value::Value(unsigned int value_):type(V_INT) 21 | { 22 | value = std::to_string(value_); 23 | } 24 | 25 | Value::Value(float value_):type(V_FLOAT) 26 | { 27 | value = std::to_string(value_); 28 | } 29 | Value::Value(double value_):type(V_DOUBLE) 30 | { 31 | value = std::to_string(value_); 32 | } 33 | Value::Value(const char* value_):type(V_STRING) 34 | { 35 | value = value_; 36 | } 37 | Value::Value(const string& value_):type(V_STRING) 38 | { 39 | value = value_; 40 | } 41 | 42 | Value &Value::operator=(bool value_) 43 | { 44 | type = V_BOOL; 45 | value = value_ ? "true":"false"; 46 | return *this; 47 | } 48 | 49 | Value &Value::operator=(int value_) 50 | { 51 | type = V_INT; 52 | value = std::to_string(value_); 53 | return *this; 54 | } 55 | 56 | Value &Value::operator=(unsigned int value_) 57 | { 58 | type = V_INT; 59 | value = std::to_string(value_); 60 | return *this; 61 | } 62 | 63 | Value &Value::operator=(float value_) 64 | { 65 | type = V_FLOAT; 66 | value = std::to_string(value_); 67 | return *this; 68 | } 69 | 70 | Value &Value::operator=(double value_) 71 | { 72 | type = V_DOUBLE; 73 | value = std::to_string(value_); 74 | return *this; 75 | } 76 | 77 | Value& Value::operator=(const char* value_) 78 | { 79 | type = V_STRING; 80 | value = value_; 81 | return *this; 82 | } 83 | 84 | Value& Value::operator=(const string &value_) 85 | { 86 | type = V_STRING; 87 | value = value_; 88 | return *this; 89 | } 90 | 91 | Value::Type Value::getType() const 92 | { 93 | return type; 94 | } 95 | 96 | bool Value::isNull() 97 | { 98 | return type == V_NULL; 99 | } 100 | bool Value::isBool() 101 | { 102 | return type == V_BOOL; 103 | } 104 | bool Value::isInt() 105 | { 106 | return type == V_INT; 107 | } 108 | bool Value::isFloat() 109 | { 110 | return type == V_FLOAT; 111 | } 112 | bool Value::isDouble() 113 | { 114 | return type == V_DOUBLE; 115 | } 116 | bool Value::isString() 117 | { 118 | return type == V_STRING; 119 | } 120 | 121 | bool Value::operator==(const Value &other) const 122 | { 123 | return type == other.type && value == other.value; 124 | } 125 | 126 | bool Value::operator!=(const Value &other) const 127 | { 128 | return type != other.type || value != other.value; 129 | } 130 | 131 | Value::operator bool() 132 | { 133 | return value == "true"; 134 | } 135 | Value::operator bool() const 136 | { 137 | return value == "true"; 138 | } 139 | Value::operator int() 140 | { 141 | return std::stoi(value); 142 | } 143 | Value::operator int() const 144 | { 145 | return std::stoi(value); 146 | } 147 | Value::operator unsigned int() 148 | { 149 | return std::stoi(value); 150 | } 151 | Value::operator unsigned int() const 152 | { 153 | return std::stoi(value); 154 | } 155 | Value::operator float() 156 | { 157 | float val; 158 | std::stringstream ss; 159 | ss << value; 160 | ss >> val; 161 | return val; 162 | } 163 | Value::operator float() const 164 | { 165 | float val; 166 | std::stringstream ss; 167 | ss << value; 168 | ss >> val; 169 | return val; 170 | } 171 | Value::operator double() 172 | { 173 | double val; 174 | std::stringstream ss; 175 | ss << value; 176 | ss >> val; 177 | return val; 178 | } 179 | Value::operator double() const 180 | { 181 | double val; 182 | std::stringstream ss; 183 | ss << value; 184 | ss >> val; 185 | return val; 186 | } 187 | Value::operator string() 188 | { 189 | return value; 190 | } 191 | Value::operator string() const 192 | { 193 | return value; 194 | } -------------------------------------------------------------------------------- /src/utility/Value.h: -------------------------------------------------------------------------------- 1 | # pragma once 2 | #include 3 | using std::string; 4 | 5 | namespace breeze::utility { 6 | 7 | class Value 8 | { 9 | public: 10 | enum Type 11 | { 12 | V_NULL = 0, 13 | V_BOOL, 14 | V_INT, 15 | V_FLOAT, 16 | V_DOUBLE, 17 | V_STRING 18 | }; 19 | //构造函数和赋值运算符重载都是为了 other -> Value 20 | 21 | Value(); 22 | Value(bool value_); 23 | Value(int value_); 24 | Value(unsigned int value_); 25 | Value(float value_); 26 | Value(double value_); 27 | Value(const char* value_); 28 | Value(const string& value_); 29 | 30 | Value& operator = (bool value_); 31 | Value& operator = (int value_); 32 | Value& operator = (unsigned int value_); 33 | Value& operator = (float value_); 34 | Value& operator = (double value_); 35 | Value& operator = (const char* value_); 36 | Value& operator = (const string& value_); 37 | 38 | ~Value() = default; 39 | 40 | Type getType () const; 41 | 42 | //获取判断 43 | bool isNull(); 44 | bool isBool(); 45 | bool isInt(); 46 | bool isFloat(); 47 | bool isDouble(); 48 | bool isString(); 49 | 50 | bool operator == (const Value& other)const; 51 | bool operator != (const Value& other)const; 52 | 53 | //Value -> other, 考虑const 和 非const 两个版本 54 | operator bool(); 55 | operator bool() const; 56 | operator int(); 57 | operator int() const; 58 | operator unsigned int(); 59 | operator unsigned int() const; 60 | operator float(); 61 | operator float() const; 62 | operator double(); 63 | operator double() const; 64 | operator string(); 65 | operator string() const; 66 | 67 | private: 68 | string value; 69 | Type type; 70 | }; 71 | 72 | } 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/utility/iniFile.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "iniFile.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace breeze::utility; 8 | 9 | IniFile::IniFile(const string& filename_) 10 | { 11 | load(filename_); 12 | } 13 | 14 | bool IniFile::load(const std::string &filename_) 15 | { 16 | sections.clear(); 17 | m_fileName = filename_; 18 | 19 | std::ifstream ifs(filename_); 20 | if(ifs.bad()) 21 | { 22 | throw std::runtime_error("fileIn is corrupted" + filename_); 23 | } 24 | if(ifs.fail()) 25 | { 26 | ifs.clear(); 27 | ifs.ignore(std::numeric_limits::max(), '\n'); 28 | throw std::runtime_error("loading ini file error" + filename_); 29 | } 30 | 31 | string line; 32 | string name; 33 | while(std::getline(ifs, line)) 34 | { 35 | line = trim(line); 36 | if(line.empty()) 37 | { 38 | continue; 39 | } 40 | if(line[0] == '#' || line[0] == ';') 41 | { 42 | continue; 43 | } 44 | if(line[0] == '[') 45 | { 46 | auto pos = line.find_first_of(']'); 47 | if(pos == std::string::npos) 48 | { 49 | throw std::runtime_error("invalid section: " + line); 50 | } 51 | name = trim(line.substr(1, pos - 1)); 52 | sections[name] = Section(); 53 | } 54 | else 55 | { 56 | auto pos = line.find_first_of('='); 57 | if(pos == std::string::npos) 58 | { 59 | throw std::runtime_error("invalid option: " + line); 60 | } 61 | string key = line.substr(0, pos); 62 | key = trim(key); 63 | string val = line.substr(pos + 1); 64 | val = trim(val); 65 | sections[name][key] = val; 66 | } 67 | } 68 | 69 | 70 | return true; 71 | } 72 | 73 | string IniFile::trim(const string &str) { 74 | if(str.empty()) 75 | { 76 | return str; 77 | } 78 | string s = str; 79 | s.erase(0, s.find_first_not_of(" \r\n")); 80 | s.erase(s.find_last_not_of(" \r\n") + 1); 81 | return s; 82 | } 83 | 84 | string IniFile::str() const{ 85 | std::stringstream ss; 86 | for(const auto& section:sections) 87 | { 88 | ss << "[" << section.first << "]" << std::endl; 89 | for(const auto& option : section.second) 90 | { 91 | ss << option.first << " = " << (string)(option.second) << std::endl; 92 | } 93 | ss << std::endl; 94 | 95 | } 96 | return ss.str(); // 关注这个函数 97 | } 98 | void IniFile::show()const 99 | { 100 | std::cout << str(); 101 | } 102 | 103 | Value& IniFile::get(const string §ion, const string &key) 104 | { 105 | return sections[section][key]; 106 | } 107 | 108 | void IniFile::set(const string §ion, const string &key, const Value &val) 109 | { 110 | sections[section][key] = val; 111 | } 112 | 113 | bool IniFile::has(const string §ion) 114 | { 115 | return sections.find(section) != sections.end(); 116 | } 117 | 118 | bool IniFile::has(const string §ion, const string &key) 119 | { 120 | auto it = sections.find(section); 121 | if(it == sections.end()) return false; 122 | 123 | return it -> second.find(key) != it -> second.end(); 124 | } 125 | 126 | void IniFile::remove(const string& section) 127 | { 128 | sections.erase(section); 129 | } 130 | void IniFile::remove(const string& section, const string& key) 131 | { 132 | auto it = sections.find(section); 133 | if(it == sections.end()) 134 | { 135 | return; 136 | } 137 | it -> second.erase(key); 138 | } 139 | 140 | void IniFile::clear() 141 | { 142 | sections.clear(); 143 | } 144 | 145 | void IniFile::save(const string &fileName) 146 | { 147 | std::ofstream ofs(fileName, std::ios::out); 148 | if(ofs.fail()) 149 | { 150 | throw std::runtime_error("saving ini file failed: " + fileName); 151 | } 152 | ofs << str(); 153 | ofs.close(); 154 | } 155 | -------------------------------------------------------------------------------- /src/utility/iniFile.h: -------------------------------------------------------------------------------- 1 | # pragma once 2 | #include "Value.h" 3 | #include 4 | #include 5 | 6 | using std::string; 7 | 8 | namespace breeze::utility 9 | { 10 | class IniFile 11 | { 12 | typedef std::map Section; 13 | public: 14 | IniFile() = default; 15 | explicit IniFile(const string& filename_); 16 | ~IniFile() = default; 17 | 18 | 19 | void show() const; 20 | bool load(const string& filename_); 21 | Value& get(const string& section, const string& key); 22 | void set(const string& section, const string& key, const Value& val); 23 | 24 | Section& operator [](const string& section) 25 | { 26 | return sections[section]; 27 | } 28 | bool has(const string& section); 29 | bool has(const string& section, const string& key); 30 | 31 | void remove(const string& section); 32 | void remove(const string& section, const string& key); 33 | 34 | void clear(); 35 | 36 | void save(const string& fileName); 37 | private: 38 | string str() const; 39 | static string trim(const string& str); 40 | 41 | private: 42 | string m_fileName; 43 | std::map sections; 44 | }; 45 | } -------------------------------------------------------------------------------- /src/utility/system.cpp: -------------------------------------------------------------------------------- 1 | #include "system.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace breeze::utility; 8 | 9 | void System::init() 10 | { 11 | core_dump(); 12 | m_root_path = get_root_path(); 13 | 14 | const string& log_path = m_root_path + "log"; 15 | DIR* dirent = opendir(log_path.c_str()); 16 | 17 | if (dirent == nullptr) 18 | { 19 | mkdir(log_path.c_str(), 0755); 20 | } 21 | else 22 | { 23 | closedir(dirent); 24 | } 25 | } 26 | 27 | string System::get_root_path() 28 | { 29 | if (!m_root_path.empty()) 30 | { 31 | return m_root_path; 32 | } 33 | 34 | char path[1024]; 35 | int len = readlink("/proc/self/exe", path, sizeof(path)); 36 | 37 | if (len < 0 || len > sizeof(path)) 38 | { 39 | return ""; 40 | } 41 | 42 | string ret = path; 43 | auto pos = ret.rfind("bin"); 44 | ret.erase(pos); 45 | 46 | m_root_path = ret; 47 | return m_root_path; 48 | } 49 | 50 | void System::core_dump() 51 | { 52 | struct rlimit x; 53 | x.rlim_cur = RLIM_INFINITY; 54 | x.rlim_max = RLIM_INFINITY; 55 | setrlimit(RLIMIT_CORE, &x); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/utility/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Singleton.h" 3 | #include 4 | using std::string; 5 | 6 | namespace breeze::utility 7 | { 8 | class System 9 | { 10 | SINGLETON(System); 11 | public: 12 | string get_root_path(); 13 | void init(); 14 | private: 15 | void core_dump(); 16 | private: 17 | string m_root_path = ""; 18 | }; 19 | } -------------------------------------------------------------------------------- /src/web/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(web 2 | DESCRIPTION "实现web相关的解析组件" 3 | ) 4 | 5 | 6 | aux_source_directory(. DIR_SRC) 7 | 8 | add_library(${PROJECT_NAME} STATIC ${DIR_SRC}) 9 | 10 | target_include_directories(${PROJECT_NAME} PUBLIC ${jsonParser_Dir} ${utility_Dir} ${frame_Dir} ${reflect_Dir}) 11 | 12 | target_link_libraries(${PROJECT_NAME} PUBLIC jsonParser reflect frame) -------------------------------------------------------------------------------- /src/web/controller.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "class_factory.h" 3 | #include "class_register.h" 4 | 5 | using namespace breeze::reflect; 6 | 7 | namespace breeze::web 8 | { 9 | class Controller: public Object 10 | { 11 | public: 12 | Controller(){} 13 | virtual ~Controller() {} 14 | }; 15 | 16 | #define CONTROLLER(classname) \ 17 | REGISTER_CLASS(classname) 18 | 19 | #define ACTION(classname, method)\ 20 | REGISTER_CLASS_METHOD(classname, method, void, const Request&, Response&) 21 | } -------------------------------------------------------------------------------- /src/web/file_upload.cpp: -------------------------------------------------------------------------------- 1 | #include "file_upload.h" 2 | #include 3 | #include "Logger.h" 4 | #include 5 | #include 6 | 7 | namespace fs = std::filesystem; 8 | 9 | using namespace breeze::web; 10 | using namespace breeze::utility; 11 | using std::string; 12 | 13 | FileUpload::FileUpload() : m_buf(nullptr), m_len(0), m_start(0), m_end(0) 14 | { 15 | 16 | } 17 | 18 | FileUpload::~FileUpload() 19 | { 20 | 21 | } 22 | 23 | string FileUpload::name() const 24 | { 25 | return m_name; 26 | } 27 | string FileUpload::filename() const 28 | { 29 | return m_filename; 30 | } 31 | string FileUpload::type() const 32 | { 33 | return m_type; 34 | } 35 | const char* FileUpload::data() const 36 | { 37 | return m_buf; 38 | } 39 | int FileUpload::size() const 40 | { 41 | return m_end - m_start; 42 | } 43 | string FileUpload::extension() const 44 | { 45 | return m_extension; 46 | } 47 | 48 | void FileUpload::parse(const char* buf, int len) 49 | { 50 | m_buf = buf; 51 | m_len = len; 52 | 53 | const char* s = buf; 54 | const char* e = buf + len - 1; 55 | const char* i = s; 56 | 57 | auto skip_chars = [&](const std::string& str){ 58 | while (i < e && str.find(*i) != std::string::npos) i ++; 59 | }; 60 | 61 | auto go_to_next_char = [&](const std::string str){ 62 | while (i < e && str.find(*i) == std::string::npos) i ++; 63 | }; 64 | 65 | // 解析 multipart/form-data boundary 66 | go_to_next_char("\r\n"); 67 | m_boundary = string(s, 2, i - s); 68 | 69 | skip_chars("\r\n"); 70 | s = i; 71 | 72 | //开始解析form data headers 73 | while (i < e) 74 | { 75 | //parse form data head 76 | go_to_next_char(": "); 77 | string name = string(s, 0, i - s); 78 | 79 | skip_chars(": "); 80 | s = i; 81 | 82 | //parse data head value 83 | go_to_next_char("\r\n"); 84 | string value = string(s, 0, i - s); 85 | 86 | m_headers[name] = value; 87 | 88 | if (name == "Content-Disposition") 89 | { 90 | //form name 91 | size_t pos = value.find("name="); 92 | size_t start = pos + 6; 93 | size_t end = value.find("\"", start); 94 | m_name = value.substr(start, end - start); 95 | 96 | log_debug("name = %s", m_name.c_str()); 97 | 98 | //filename 99 | pos = value.find("filename="); 100 | start = pos + 10; 101 | end = value.find("\"", start); 102 | m_filename = value.substr(start, end - start); 103 | 104 | log_debug("filename=", m_filename.c_str()); 105 | 106 | size_t ext = m_filename.find("."); 107 | m_extension = m_filename.substr(ext); 108 | } 109 | 110 | else if (name == "Content-Type") 111 | { 112 | m_type = value; 113 | } 114 | 115 | if (strncmp(i, "\r\n\r\n", 4) == 0) 116 | { 117 | i += 4; 118 | break; 119 | } 120 | skip_chars("\r\n"); 121 | 122 | s = i; 123 | } 124 | 125 | //metadata parse over 126 | 127 | m_start = i - buf; 128 | string str = "--" + m_boundary + "--"; 129 | const char* t = e - str.size(); 130 | m_end = t - m_buf; 131 | log_debug("upload content: %s", s); 132 | 133 | } 134 | 135 | bool FileUpload::save(const std::string& filename) 136 | { 137 | fs::path filepath = filename; 138 | if (!fs::exists(filepath.parent_path())) 139 | { 140 | try 141 | { 142 | fs::create_directories(filepath.parent_path()); 143 | } 144 | catch (const std::exception& e) 145 | { 146 | log_error("Error creating directories: %s", e.what()); 147 | return false; 148 | } 149 | } 150 | 151 | std::ofstream ofs(filepath); 152 | 153 | if (ofs.fail()) 154 | { 155 | log_error("open file failed: %s", filename.c_str()); 156 | return false; 157 | } 158 | 159 | ofs.write(m_buf + m_start, m_end - m_start); 160 | ofs.flush(); 161 | return true; 162 | } -------------------------------------------------------------------------------- /src/web/file_upload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace breeze::web 6 | { 7 | class FileUpload 8 | { 9 | public: 10 | FileUpload(); 11 | ~FileUpload(); 12 | 13 | std::string name() const; 14 | std::string filename() const; 15 | std::string type() const; 16 | const char* data() const; 17 | int size() const; 18 | std::string extension() const; 19 | 20 | void parse(const char* buf, int len); 21 | bool save(const std::string& filename); 22 | 23 | 24 | private: 25 | std::string m_name; 26 | std::string m_type; 27 | std::string m_filename; 28 | std::string m_extension; 29 | std::string m_boundary; 30 | std::map m_headers; 31 | const char* m_buf; 32 | int m_len; 33 | int m_start; 34 | int m_end; 35 | }; 36 | } -------------------------------------------------------------------------------- /src/web/request.cpp: -------------------------------------------------------------------------------- 1 | #include "request.h" 2 | #include "file_upload.h" 3 | #include "response.h" 4 | #include 5 | #include 6 | #include 7 | #include "String.h" 8 | #include "Logger.h" 9 | 10 | using namespace breeze::web; 11 | using namespace breeze::utility; 12 | 13 | int Request::parse_header(const char* buf, int len) 14 | { 15 | /* 解析method uri protocol*/ 16 | const char* start = buf; 17 | const char* end = buf + len - 1; 18 | const char* i = start; 19 | 20 | auto skip_chars = [&](const string& str){ 21 | while (i < end && str.find(*i) != string::npos) i++; 22 | }; 23 | 24 | auto go_to_next_char = [&](const string str){ 25 | while (i < end && str.find(*i) == string::npos) i ++; 26 | }; 27 | 28 | 29 | //解析method 30 | skip_chars(" "); 31 | 32 | start = i; //定位起点 33 | go_to_next_char(" "); 34 | 35 | m_method = string(start, 0, i - start); 36 | 37 | //解析 uri 38 | skip_chars(" "); 39 | start = i; 40 | go_to_next_char(" "); 41 | m_uri = string(start, 0, i - start); 42 | 43 | //解析protocol 44 | skip_chars(" "); 45 | start = i; 46 | go_to_next_char("\r\n"); 47 | m_protocol = string(start, 0, i - start); 48 | 49 | skip_chars("\r\n"); 50 | 51 | //如果uri 存在 ? 需要进一步处理 52 | 53 | auto pos = m_uri.find('?'); 54 | if (pos != string::npos) 55 | { 56 | m_path = m_uri.substr(0, pos); 57 | m_query_string = m_uri.substr(pos + 1); 58 | std::vector querys = String::split(m_query_string, '&'); 59 | for (auto query : querys) 60 | { 61 | auto split_pos = query.find('='); 62 | string left = query.substr(0, split_pos); 63 | string right = split_pos < query.size() - 1 ? query.substr(split_pos + 1) : ""; 64 | m_gets[left] = right; 65 | } 66 | } 67 | else 68 | { 69 | m_path = m_uri; 70 | } 71 | 72 | //解析header 73 | while (i < end) 74 | { 75 | start = i; 76 | go_to_next_char(": "); 77 | string name = string(start, 0, i - start); 78 | 79 | skip_chars(": "); 80 | start = i; 81 | go_to_next_char("\r\n"); 82 | string value = string(start, 0, i - start); 83 | 84 | m_headers[name] = value; 85 | 86 | //结束 87 | if (strncmp(i, "\r\n\r\n", 4) == 0) 88 | { 89 | i += 4; 90 | break; 91 | } 92 | 93 | skip_chars("\r\n"); 94 | } 95 | return i - buf; 96 | } 97 | 98 | int Request::parse_body(const char* buf, int len) 99 | { 100 | const string& content_type = header("Content-Type"); 101 | if (content_type.find("application/json") != string::npos) 102 | { 103 | log_debug("body data = %s", buf); 104 | m_post.parse(buf, len); 105 | } 106 | 107 | else if (content_type.find("multipart/form-data") != std::string::npos) 108 | { 109 | log_debug("ready to upload"); 110 | FileUpload upload; 111 | upload.parse(buf, len); 112 | 113 | m_files[upload.name()] = upload; 114 | } 115 | 116 | return 0; 117 | } 118 | 119 | 120 | bool Request::is_get() const 121 | { 122 | return m_method == "GET"; 123 | } 124 | bool Request::is_post() const 125 | { 126 | return m_method == "POST"; 127 | } 128 | bool Request::is_delete() const 129 | { 130 | return m_method == "DELETE"; 131 | } 132 | 133 | string Request::get(const string& name) const 134 | { 135 | auto it = m_gets.find(name); 136 | if (it == m_gets.end()) 137 | { 138 | return ""; 139 | } 140 | return it -> second; 141 | } 142 | 143 | Json Request::post(const string& name) const 144 | { 145 | if (name.empty()) 146 | { 147 | return m_post; 148 | } 149 | 150 | return m_post.get(name); 151 | } 152 | 153 | 154 | string Request::header(const string& name) const 155 | { 156 | auto it = m_headers.find(name); 157 | if (it == m_headers.end()) 158 | { 159 | return ""; 160 | } 161 | return it -> second; 162 | } 163 | 164 | string Request::cookie(const string& name) const 165 | { 166 | auto it = m_cookies.find(name); 167 | if (it == m_cookies.end()) 168 | { 169 | return ""; 170 | } 171 | 172 | return it -> second; 173 | } 174 | string Request::path() const 175 | { 176 | return m_path; 177 | } 178 | string Request::user_agent() const 179 | { 180 | return header("User-Agent"); 181 | } 182 | string Request::user_host() const 183 | { 184 | return header("Host"); 185 | } 186 | 187 | long Request::content_length() const 188 | { 189 | return m_headers.find("Content-Length") != m_headers.end() ? std::stol(header("Content-Length")) : 0; 190 | } 191 | 192 | void Request::show() const 193 | { 194 | log_debug("http method: %s", m_method.c_str()); 195 | log_debug("http uri: %s", m_uri.c_str()); 196 | log_debug("http protocol: %s", m_protocol.c_str()); 197 | log_debug("http path: %s", m_path.c_str()); 198 | log_debug("http query string: %s", m_query_string.c_str()); 199 | log_debug("http headers <<---"); 200 | for (auto header : m_headers) 201 | { 202 | log_debug("%s: %s", header.first.c_str(),header.second.c_str()); 203 | } 204 | log_debug("--->>"); 205 | log_debug("http get paraments <<---"); 206 | for (auto get : m_gets) 207 | { 208 | log_debug("%s = %s", get.first.c_str(), get.second.c_str()); 209 | } 210 | log_debug("---->>"); 211 | 212 | log_debug("http post paraments <<---"); 213 | log_debug("%s", m_post.str().c_str()); 214 | log_debug("---->>"); 215 | 216 | log_debug("http body: \n%s", m_body.c_str()); 217 | } 218 | 219 | FileUpload Request::file(const string& name) const 220 | { 221 | auto it = m_files.find(name); 222 | if (it == m_files.end()) 223 | { 224 | return FileUpload(); 225 | } 226 | 227 | return it -> second; 228 | } -------------------------------------------------------------------------------- /src/web/request.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "Json.h" 5 | #include "file_upload.h" 6 | 7 | using namespace breeze::json; 8 | 9 | using std::string; 10 | 11 | namespace breeze::web 12 | { 13 | class Request 14 | { 15 | public: 16 | Request() = default; 17 | ~Request() = default; 18 | 19 | int parse_header(const char* buf, int len); 20 | int parse_body(const char* buf, int len); 21 | 22 | bool is_get() const; 23 | bool is_post() const; 24 | bool is_delete() const; 25 | 26 | string get(const string& name) const; 27 | Json post(const string& name = "") const; 28 | 29 | FileUpload file(const string& name) const; 30 | string header(const string& name) const; 31 | string cookie(const string& name) const; 32 | string path() const; 33 | string user_agent() const; 34 | string user_host() const; 35 | 36 | long content_length() const; 37 | void show() const; 38 | 39 | private: 40 | string m_method; 41 | string m_uri; 42 | string m_protocol; 43 | string m_path; 44 | string m_body; 45 | string m_query_string; 46 | std::map m_gets; 47 | Json m_post; 48 | std::map m_headers; 49 | std::map m_cookies; 50 | std::map m_files; 51 | }; 52 | } -------------------------------------------------------------------------------- /src/web/response.cpp: -------------------------------------------------------------------------------- 1 | #include "response.h" 2 | #include "Logger.h" 3 | #include "server.h" 4 | #include "system.h" 5 | #include 6 | #include 7 | 8 | using namespace breeze::web; 9 | using namespace breeze::utility; 10 | 11 | Response::Response() : m_code(200) 12 | { 13 | 14 | } 15 | Response::~Response() 16 | { 17 | 18 | } 19 | 20 | void Response::code(int code) 21 | { 22 | m_code = code; 23 | } 24 | void Response::data(Type type, const string& data) 25 | { 26 | m_type = type; 27 | m_data = data; 28 | } 29 | 30 | string Response::data() const 31 | { 32 | std::ostringstream oss; 33 | oss << "HTTP/1.1 " << m_code << " OK\r\n"; 34 | switch (m_type) 35 | { 36 | case Type::HTML: 37 | oss << "Content-Type: text/html; charset: utf-8\r\n"; 38 | break; 39 | case Type::JSON: 40 | oss << "Content-Type: application/json; charset: utf-8\r\n"; 41 | break; 42 | case Type::JS: 43 | oss << "Content-Type: text/javascript; charset: utf-8\r\n"; 44 | break; 45 | case Type::CSS: 46 | oss << "Content-Type: text/css; charset: utf-8\r\n"; 47 | break; 48 | case Type::JPG: 49 | oss << "Content-Type: image/jpeg; charset: utf-8\r\n"; 50 | break; 51 | case Type::PNG: 52 | oss << "Content-Type: image/png; charset: utf-8\r\n"; 53 | break; 54 | case Type::GIF: 55 | oss << "Content-Type: image/gif; charset: utf-8\r\n"; 56 | break; 57 | case Type::ICO: 58 | oss << "Content-Type: image/x-icon; charset: utf-8\r\n"; 59 | break; 60 | default: 61 | break; 62 | } 63 | oss << "Content-Length: " << m_data.size() << "\r\n\r\n"; 64 | oss << m_data << "\r\n"; 65 | return oss.str(); 66 | } 67 | 68 | void Response::html(const string& data) 69 | { 70 | m_type = Type::HTML; 71 | m_data = data; 72 | } 73 | void Response::json(const string& data) 74 | { 75 | m_type = Type::JSON; 76 | m_data = data; 77 | } 78 | 79 | string Response::page_not_found() 80 | { 81 | auto sys = Singleton::Instance(); 82 | string file_path = sys -> get_root_path() + "/template/404.html"; 83 | std::ifstream in(file_path); 84 | 85 | std::ostringstream content; 86 | content << in.rdbuf(); 87 | string data = content.str(); 88 | 89 | std::ostringstream response; 90 | 91 | response << "HTTP/1.1 404 Not Found\r\n"; 92 | response << "Content-Type: text/html; charset: utf-8\r\n"; 93 | response << "Content-Length: " << data.size() << "\r\n\r\n"; 94 | response << data << "\r\n"; 95 | 96 | 97 | return response.str(); 98 | } 99 | 100 | void Response::render(const string& file_name) 101 | { 102 | const string& template_path = Singleton::Instance() ->get_template_folder(); 103 | const string& file_path = template_path + "/" + file_name; 104 | 105 | log_debug("template path : %s", file_path.c_str()); 106 | 107 | std::ifstream ifs(file_path); 108 | 109 | if (ifs.fail()) 110 | { 111 | return; 112 | } 113 | 114 | std::ostringstream oss; 115 | oss << ifs.rdbuf(); 116 | m_type = Type::HTML; 117 | m_data = oss.str(); 118 | } -------------------------------------------------------------------------------- /src/web/response.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | using std::string; 4 | 5 | namespace breeze::web 6 | { 7 | 8 | class Response 9 | { 10 | public: 11 | enum class Type 12 | { 13 | HTML, 14 | JSON, 15 | JS, 16 | CSS, 17 | JPG, 18 | PNG, 19 | GIF, 20 | ICO 21 | }; 22 | 23 | Response(); 24 | ~Response(); 25 | 26 | void code(int code); 27 | void data(Type type, const string& data); 28 | string data() const; 29 | 30 | void html(const string& data); 31 | void json(const string& data); 32 | 33 | static string page_not_found(); 34 | void render(const string& file_name); 35 | 36 | private: 37 | int m_code; 38 | Type m_type; 39 | string m_data; 40 | }; 41 | } -------------------------------------------------------------------------------- /src/web/server.cpp: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | #include "../frame/server.h" 3 | #include "Logger.h" 4 | #include "class_factory.h" 5 | #include "response.h" 6 | #include "String.h" 7 | #include "system.h" 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace breeze; 13 | using namespace breeze::web; 14 | 15 | void Server::start() 16 | { 17 | try 18 | { 19 | Singleton::Instance() -> start(); 20 | } 21 | catch (const std::exception& e) 22 | { 23 | std::cout << e.what() << std::endl; 24 | } 25 | } 26 | 27 | 28 | void Server::bind(const string& path, server_handler handler) 29 | { 30 | m_handlers[path] = handler; 31 | } 32 | 33 | string Server::handle(const Request& req) 34 | { 35 | const string& path = req.path(); 36 | 37 | auto it = m_handlers.find(path); 38 | 39 | if (it != m_handlers.end()) 40 | { 41 | Response resp; 42 | it -> second(req, resp); 43 | return resp.data(); 44 | } 45 | 46 | using Type = Response::Type; 47 | 48 | auto func = [](const string& file_path, Response::Type type){ 49 | std::ifstream ifs(file_path); 50 | 51 | std::ostringstream oss; 52 | oss << ifs.rdbuf(); 53 | ifs.close(); 54 | 55 | Response resp; 56 | resp.data(type, oss.str()); 57 | 58 | return resp.data(); 59 | }; 60 | 61 | auto bin_func = [](const string& file_path, Response::Type type){ 62 | std::ifstream ifs(file_path, std::ios::binary); 63 | 64 | std::ostringstream oss; 65 | oss << ifs.rdbuf(); 66 | ifs.close(); 67 | 68 | Response resp; 69 | resp.data(type, oss.str()); 70 | 71 | return resp.data(); 72 | }; 73 | 74 | 75 | if (String::hasSuffix(path, ".html")) 76 | { 77 | const string& file_path = get_template_folder() + path; 78 | 79 | return func(file_path, Type::HTML); 80 | } 81 | else if (String::hasSuffix(path, ".js")) 82 | { 83 | const string& file_path = get_static_folder() + path; 84 | 85 | return func(file_path, Type::JS); 86 | } 87 | else if (String::hasSuffix(path, ".css")) 88 | { 89 | const string& file_path = get_static_folder() + path; 90 | 91 | return func(file_path, Type::CSS); 92 | } 93 | else if (String::hasSuffix(path, ".jpg")) 94 | { 95 | const string& file_path = get_static_folder() + path; 96 | 97 | return bin_func(file_path, Type::JPG); 98 | } 99 | else if (String::hasSuffix(path, ".png")) 100 | { 101 | const string& file_path = get_static_folder() + path; 102 | 103 | return bin_func(file_path, Type::PNG); 104 | } 105 | else if (String::hasSuffix(path, ".gif")) 106 | { 107 | const string& file_path = get_static_folder() + path; 108 | 109 | return bin_func(file_path, Type::GIF); 110 | } 111 | else if (String::hasSuffix(path, ".ico")) 112 | { 113 | const string& file_path = get_static_folder() + path; 114 | 115 | return bin_func(file_path, Type::ICO); 116 | } 117 | 118 | log_debug("enter controller auto"); 119 | 120 | string classname; 121 | string methodname; 122 | 123 | auto arr = String::split(String::trim(path, " /"), '/'); 124 | 125 | log_debug("path = %s", path.c_str()); 126 | 127 | if (arr.size() == 0) //默认 128 | { 129 | 130 | classname = "Index"; 131 | methodname = "index"; 132 | } 133 | else if (arr.size() == 1) 134 | { 135 | classname = String::capitalize(arr[0]); 136 | methodname = "index"; 137 | } 138 | 139 | else if (arr.size() == 2) // 一个控制器,一个方法 140 | { 141 | classname = String::capitalize(arr[0]); 142 | methodname = arr[1]; 143 | } 144 | 145 | log_debug("classname = %s, methodname = %s", classname.c_str(), methodname.c_str()); 146 | 147 | auto factory = Singleton::Instance(); 148 | 149 | if (factory -> map_is_empty()) 150 | { 151 | log_error("something wrong happens during reflecting, class_map is empty"); 152 | } 153 | 154 | auto ctrl = factory -> create_class(classname); 155 | 156 | if (ctrl == nullptr) 157 | { 158 | log_error("Failed to get controller, name: %s", classname.c_str()); 159 | return Response::page_not_found(); 160 | } 161 | 162 | auto method = factory -> get_class_method(classname, methodname); 163 | if (method == nullptr) 164 | { 165 | log_error("Failed to get method, controller name: %s, method name: %s", classname.c_str(), methodname.c_str()); 166 | delete ctrl; 167 | return Response::page_not_found(); 168 | } 169 | 170 | log_debug("Succeed getting controller and method"); 171 | 172 | try 173 | { 174 | Response resp; 175 | ctrl -> call(methodname, req, resp); 176 | delete ctrl; 177 | return resp.data(); 178 | } 179 | 180 | catch (const std::exception& e) 181 | { 182 | if (ctrl) 183 | { 184 | delete ctrl; 185 | } 186 | Response resp; 187 | resp.code(404); 188 | resp.html(e.what()); 189 | 190 | return resp.data(); 191 | } 192 | 193 | log_error("Failed to call"); 194 | return Response::page_not_found(); 195 | } 196 | 197 | void Server::set_template_folder(const string& template_folder) 198 | { 199 | m_template_folder = template_folder; 200 | } 201 | 202 | string Server::get_template_folder() const 203 | { 204 | const string& root_path = Singleton::Instance() -> get_root_path(); 205 | string folder_path = root_path + "template"; 206 | return folder_path; 207 | } 208 | 209 | void Server::set_static_folder(const string& static_folder) 210 | { 211 | m_static_folder = static_folder; 212 | } 213 | string Server::get_static_folder()const 214 | { 215 | const string& root_path = Singleton::Instance() -> get_root_path(); 216 | string folder_path = root_path + "static"; 217 | return folder_path; 218 | } -------------------------------------------------------------------------------- /src/web/server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Singleton.h" 4 | #include "request.h" 5 | #include "response.h" 6 | #include 7 | #include 8 | using namespace breeze::utility; 9 | 10 | namespace breeze::web 11 | { 12 | typedef std::function server_handler; 13 | 14 | class Server 15 | { 16 | SINGLETON(Server); 17 | 18 | public: 19 | void start(); 20 | 21 | void bind(const string& path, server_handler handler); 22 | string handle(const Request& req); 23 | 24 | void set_template_folder(const string& template_folder); 25 | string get_template_folder() const; 26 | 27 | void set_static_folder(const string& static_folder); 28 | string get_static_folder()const; 29 | 30 | private: 31 | std::map m_handlers; 32 | string m_template_folder = "template"; 33 | string m_static_folder = "static" ; 34 | }; 35 | } -------------------------------------------------------------------------------- /static/indexstyles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Arial', sans-serif; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | height: 100vh; 7 | margin: 0; 8 | background: linear-gradient(135deg, #689bb9 0%, #e166d7 100%); 9 | color: #fff; 10 | } 11 | 12 | h1 { 13 | font-size: 4em; 14 | text-align: center; 15 | text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); 16 | border-bottom: 3px solid #fff; 17 | padding-bottom: 20px; 18 | transition: transform 0.5s ease-in-out, color 0.5s ease-in-out, text-shadow 0.5s ease-in-out; 19 | animation: color-change 5s infinite; 20 | } 21 | 22 | h1:hover { 23 | transform: scale(1.2); 24 | color: #ffd700; 25 | text-shadow: 4px 4px 8px rgba(0, 0, 0, 0.5); 26 | } 27 | 28 | @keyframes color-change { 29 | 0% { color: #ff9ad8; } 30 | 25% { color: #cec4fa; } 31 | 50% { color: #ff9a9e; } 32 | 75% { color: #fad0c4; } 33 | 100% { color: #4a2e9c; } 34 | } 35 | -------------------------------------------------------------------------------- /static/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/static/logo.jpg -------------------------------------------------------------------------------- /static/man.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/static/man.jpg -------------------------------------------------------------------------------- /static/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Arial', sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | background-color: #f8f9fa; 6 | color: #333; 7 | background-image: url('/background.png'); 8 | background-size: cover; 9 | background-attachment: fixed; 10 | } 11 | header { 12 | background-color: #007bff; 13 | color: white; 14 | padding: 20px 0; 15 | text-align: center; 16 | position: relative; 17 | } 18 | .logo { 19 | position: absolute; 20 | left: 20px; 21 | top: 50%; 22 | transform: translateY(-50%); 23 | width: 50px; 24 | height: 50px; 25 | } 26 | nav { 27 | display: flex; 28 | justify-content: center; 29 | background-color: #343a40; 30 | } 31 | nav a { 32 | color: white; 33 | padding: 14px 20px; 34 | text-decoration: none; 35 | text-align: center; 36 | } 37 | nav a:hover { 38 | background-color: #495057; 39 | } 40 | .container { 41 | max-width: 800px; 42 | margin: 20px auto; 43 | padding: 20px; 44 | background-color: white; 45 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 46 | } 47 | .container h2 { 48 | color: #007bff; 49 | } 50 | .user-list { 51 | margin: 20px 0; 52 | } 53 | .user-list table { 54 | width: 100%; 55 | border-collapse: collapse; 56 | } 57 | .user-list th, .user-list td { 58 | border: 1px solid #ddd; 59 | padding: 8px; 60 | text-align: left; 61 | } 62 | .user-list th { 63 | background-color: #f2f2f2; 64 | } 65 | .user-list tr:nth-child(even) { 66 | background-color: #f9f9f9; 67 | } 68 | .form-group { 69 | margin-bottom: 15px; 70 | } 71 | .form-group label { 72 | display: block; 73 | margin-bottom: 5px; 74 | } 75 | .form-group input { 76 | width: 100%; 77 | padding: 8px; 78 | box-sizing: border-box; 79 | } 80 | .button { 81 | display: inline-block; 82 | padding: 10px 20px; 83 | font-size: 16px; 84 | cursor: pointer; 85 | text-align: center; 86 | text-decoration: none; 87 | outline: none; 88 | color: #fff; 89 | background-color: #007bff; 90 | border: none; 91 | border-radius: 5px; 92 | box-shadow: 0 4px #0056b3; 93 | } 94 | .button:hover { 95 | background-color: #0056b3; 96 | } 97 | .button:active { 98 | background-color: #0056b3; 99 | box-shadow: 0 2px #004494; 100 | transform: translateY(2px); 101 | } 102 | footer { 103 | background-color: #343a40; 104 | color: white; 105 | text-align: center; 106 | padding: 10px 0; 107 | position: fixed; 108 | bottom: 0; 109 | width: 100%; 110 | } 111 | -------------------------------------------------------------------------------- /static/upload.js: -------------------------------------------------------------------------------- 1 | document.getElementById('uploadForm').addEventListener('submit', function (event) { 2 | event.preventDefault(); 3 | const formData = new FormData(event.target); 4 | const xhr = new XMLHttpRequest(); 5 | const progressBar = document.getElementById('progressBar'); 6 | const progressBarFill = progressBar.querySelector('div'); 7 | progressBar.style.display = 'block'; 8 | xhr.open('POST', '/file/upload', true); 9 | 10 | xhr.upload.addEventListener('progress', function (e) { 11 | if (e.lengthComputable) { 12 | const percentComplete = (e.loaded / e.total) * 100; 13 | progressBarFill.style.width = percentComplete + '%'; 14 | progressBarFill.textContent = Math.round(percentComplete) + '%'; 15 | } 16 | }); 17 | 18 | xhr.onload = function () { 19 | if (xhr.status === 200) { 20 | const response = JSON.parse(xhr.responseText); 21 | progressBarFill.style.width = '100%'; 22 | progressBarFill.textContent = '100%'; 23 | document.getElementById('uploadResult').innerHTML = ` 24 |
25 | 文件上传成功!
26 | 文件名:${response.filename}
27 | 文件大小:${(response.size / 1024).toFixed(2)} KB 28 |
29 | `; 30 | } else { 31 | document.getElementById('uploadResult').innerHTML = ` 32 |
33 | 文件上传失败,请重试。 34 |
35 | `; 36 | } 37 | progressBar.style.display = 'none'; 38 | }; 39 | 40 | xhr.send(formData); 41 | }); 42 | -------------------------------------------------------------------------------- /static/woman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breeze-wink/mvc-webserver/ef804f2e7594a072f6e29c08a6c2734d0f1005ae/static/woman.jpg -------------------------------------------------------------------------------- /template/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 Not Found 7 | 46 | 47 | 48 |
49 |

404

50 |

Page Not Found

51 |

Sorry, the page you are looking for does not exist.

52 | Go to Homepage 53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /template/file/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 文件上传 7 | 124 | 125 | 126 | 127 |
128 |

文件上传

129 |

请选择要上传的文件:

130 |
131 |
132 | 133 |
134 |
135 |
136 |
137 |
138 |
139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /template/index/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | hello 7 | 8 | 9 | 10 |

Hello World

11 | 12 | 13 | -------------------------------------------------------------------------------- /template/index/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Index 7 | 8 | 9 | 10 |

Welcome to my Web

11 | 12 | 13 | -------------------------------------------------------------------------------- /template/user/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | User Management System 7 | 8 | 9 | 10 |
11 | 12 |

User Management System

13 |
14 | 15 | 21 | 22 |
23 |
24 |

User List

25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
IDNameEmail
1John Doejohn.doe@example.com
2Jane Smithjane.smith@example.com
47 |
48 |
49 | 50 |
51 |

Add New User

52 |
53 |
54 | 55 | 56 |
57 |
58 | 59 | 60 |
61 | 62 |
63 |
64 |
65 | 66 |
67 |

© 2024 User Management System

68 |
69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /template/user/happy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 |
11 | 12 |
13 |

一分钟注册,享一辈子幸福!

14 |
15 |
16 |
17 | 性别 18 | 22 | 26 |
27 |
28 |
29 | 33 |
34 |
35 |
36 |
37 | 生日 38 |
39 | 40 | 41 | 42 |
43 |
44 |
45 | 所在地区 46 |
47 | 48 |
49 |
50 |
51 | 婚姻状况 52 | 55 | 56 | 57 |
58 |
59 | 免费注册 60 |
61 | 64 |
65 | 66 | 67 | 68 | 140 | -------------------------------------------------------------------------------- /template/user/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | User Management 7 | 98 | 99 | 100 | 101 |
102 |

User Management System

103 |
104 | 105 | 111 | 112 |
113 |
114 |

User List

115 |
116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 |
IDNameEmail
1John Doejohn.doe@example.com
2Jane Smithjane.smith@example.com
138 |
139 |
140 | 141 |
142 |

Add New User

143 |
144 |
145 | 146 | 147 |
148 |
149 | 150 | 151 |
152 | 153 |
154 |
155 |
156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(test 2 | DESCRIPTION "给一些新增的小模块在并入项目前进行简单的使用测试" 3 | ) 4 | 5 | set(test_output_path ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test) 6 | 7 | add_subdirectory(web_parse_request) -------------------------------------------------------------------------------- /test/web_parse_request/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(http_request_test 2 | DESCRIPTION "测试对http请求的解析" 3 | ) 4 | 5 | add_executable(${PROJECT_NAME} request_test.cpp) 6 | 7 | target_include_directories(${PROJECT_NAME} PUBLIC ${SRC_DIR}/web ${SRC_DIR}/jsonParser ${SRC_DIR}/utility) 8 | 9 | target_link_libraries(${PROJECT_NAME} PUBLIC web utility jsonParser) 10 | 11 | set_target_properties(${PROJECT_NAME} PROPERTIES 12 | RUNTIME_OUTPUT_DIRECTORY ${test_output_path} 13 | ) -------------------------------------------------------------------------------- /test/web_parse_request/http_request.txt: -------------------------------------------------------------------------------- 1 | POST /submit-form?a=1&b=2&c=3 HTTP/1.1 2 | Host: www.example.com 3 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 4 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 5 | Accept-Language: en-US,en;q=0.5 6 | Accept-Encoding: gzip, deflate 7 | Content-Type: application/x-www-form-urlencoded 8 | Content-Length: 27 9 | Connection: keep-alive 10 | 11 | name=John+Doe&age=23&city=NY 12 | -------------------------------------------------------------------------------- /test/web_parse_request/request_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "Logger.h" 6 | #include "request.h" 7 | 8 | using namespace breeze::web; 9 | using namespace breeze::utility; 10 | 11 | int main() 12 | { 13 | Singleton::Instance() -> open("/home/breeze1/mvc-webserver/bin/test/test.log"); 14 | 15 | int fd = open("/home/breeze1/mvc-webserver/test/web_parse_request/http_request.txt", O_RDONLY, 0644); 16 | char msg[1024] = {0}; 17 | 18 | struct stat st; 19 | fstat(fd, &st); 20 | 21 | read(fd, msg, st.st_size); 22 | 23 | printf("%s\n", msg); 24 | 25 | Request req; 26 | req.parse_header(msg, sizeof(msg)); 27 | 28 | req.show(); 29 | 30 | close(fd); 31 | return 0; 32 | } --------------------------------------------------------------------------------