1.Git拉取项目
编译一下,通过了,虽然会出现报错,但是经常写虚幻C++的人应该对此见怪不怪了吧
2. 实现main.cpp
io_context 所有的异步操作(如网络,信号) 都通过它来调度
注册信号处理器,监听系统终止信号
创建服务器对象实时的监听8080端口是否有客户端请求连接,如果有客户端请求连接,将该连接实例化成一个对象的方式来管理连接中的处理,服务器本身继续监听8080端口是否有新的客户端连接
在连接对象中来处理客户端的读事件
一个端口能够支持成千上万个并发连接
#include <iostream>#include "CServer.h"
#include "HttpConnection.h"
#include "LogicSystem.h"int main()
{try {unsigned short port = static_cast<unsigned short>(8080); // 指定服务器监听的端口号net::io_context ioc(1); // 异步操作的引擎,所有事件循环都通过他来操作,单线程boost::asio::signal_set signals(ioc, SIGINT, SIGTERM); // 信号处理,用于退出程序// 绑定信号的回调函数signals.async_wait([&ioc](const boost::system::error_code& error, int sognalNumber){if (error)return;ioc.stop();});std::shared_ptr<CServer> server = std::make_shared<CServer> (ioc, port);server->Start();ioc.run();}catch (std::exception& e) {std::cerr << "Exception: " << e.what() << "\n";return EXIT_FAILURE;}
}
3.编译并且测试服务器是否有效
3.1 测试连接是否有效
输入localhost:8080/getTest
3.2 查询出错原因
crtl + T 文本形式搜索
如果当前对象不是通过std::make_shared<CServer>创建的,调用shared_from_this()就会抛出这个异常
将上述代码修改为
发现即便修改了还是没用,就算是不继承std::enable_shared_from_this<HttpConnection>还是会报错,对不起了,我自己来管理指针,将全部的智能指针删除,直接用裸指针,在使用裸指针时要
自己来管理if判断一下,判断是否为空
全局使用别的对象指针的只有这一个地方
4.解析URL
将一个十六进制的字符转换成对应的数字(0~15)
unsigned char HttpConnection::FromHex(unsigned char c)
{unsigned char y;if(c >= 'A' && c <= 'Z')y = c - 'A' + 10;else if(c >= 'a' && c <= 'z')y = c - 'a' + 10;elsey = c - '0';return y;
}
把一个数字(0~15)转换为它对应的十六进制字符
unsigned char HttpConnection::ToHex(unsigned char c)
{return c > 9 ? c + 55 : c + 48;
}
将输入字符串转换成URL安全的格式
string HttpConnection::UrlEncode(const string& str)
{string strTemp = "";size_t len = str.length();for (size_t i = 0; i < len; i++){if (isalnum((unsigned char)str[i])|| str[i] == '-'|| str[i] == '_'|| str[i] == '.'|| str[i] == '~'){strTemp += str[i];}else{strTemp += '%';strTemp += ToHex((unsigned char)str[i] >> 4);strTemp += ToHex((unsigned char)str[i] & 0x0F);}}return strTemp;
}
将URL转换成字符串
string HttpConnection::UrlDecode(const string& str)
{std::string strTemp = "";size_t length = str.length();for (size_t i = 0; i < length; i++){//还原+为空if (str[i] == '+') strTemp += ' ';//遇到%将后面的两个字符从16进制转为char再拼接else if (str[i] == '%'){assert(i + 2 < length);unsigned char high = FromHex((unsigned char)str[++i]);unsigned char low = FromHex((unsigned char)str[++i]);strTemp += high * 16 + low;}else strTemp += str[i];}return strTemp;
}
将URL参数解析成map,方便后面直接用键取值
/* 将URL参数解析成map,方便后面直接用键取值 */
void HttpConnection::PreParseGetParam()
{// 提取 URI auto uri = _request.target();// 查找查询字符串的开始位置(即 '?' 的位置) auto query_pos = uri.find('?');if (query_pos == std::string::npos) {_getUrl = uri;return;}_getUrl = uri.substr(0, query_pos);std::string query_string = uri.substr(query_pos + 1);std::string key;std::string value;size_t pos = 0;while ((pos = query_string.find('&')) != std::string::npos) {auto pair = query_string.substr(0, pos);size_t eq_pos = pair.find('=');if (eq_pos != std::string::npos) {key = UrlDecode(pair.substr(0, eq_pos)); // 假设有 url_decode 函数来处理URL解码 value = UrlDecode(pair.substr(eq_pos + 1));_getParams[key] = value;}query_string.erase(0, pos + 1);}// 处理最后一个参数对(如果没有 & 分隔符) if (!query_string.empty()) {size_t eq_pos = query_string.find('=');if (eq_pos != std::string::npos) {key = UrlDecode(query_string.substr(0, eq_pos));value = UrlDecode(query_string.substr(eq_pos + 1));_getParams[key] = value;}}
}
这些东西根本记不住,也没必要,问一下AI就行了,毕竟这种接口给它当成原子操作用就完事了
5.处理请求时解析URL
5.1 找到处理连接客户端请求的函数
看这个数据是什么时候获取的,应该是当检测到数据到来时会自动的将数据存储到_request中
所以如果URL是get请求就解析他
/* 处理客户端的请求数据 */
void HttpConnection::HandleRequest()
{_response.version(_request.version());_response.keep_alive(false); // 处理完该请求后断开连接if (_request.method() == http::verb::get) // 如果是Http的Get请求{PreParseGetParam(); // 解析客户端发来的URL请求bool IsSucceed = LogicSystem::GetInstance()->HandleGet(_getUrl, this);if (!IsSucceed){_response.result(http::status::not_found);_response.set(http::field::content_type, "text/plain");beast::ostream(_response.body()) << "url not found\r\n";WriteResponse();return;}_response.result(http::status::ok);_response.set(http::field::server, "GateServer");WriteResponse();return;}
}
将解析出来的数据返回出去
LogicSystem::LogicSystem()
{RegisterGet("/getTest", [](HttpConnection* connection){if (connection){beast::ostream(connection->_response.body()) << "receive getTest request"; // 向 Http响应体中写入内容for (auto content : connection->_getParams){beast::ostream(connection->_response.body()) << content.first << " : " << content.second << std::endl; // 向 Http响应体中写入内容}}else{std::cout << "connection is null" << std::endl;}});
}
6. 注册客户端Post请求
void RegisterPost(std::string, HttpHandler); // 注册客户端Post请求void LogicSystem::RegisterPost(std::string url, HttpHandler handler)
{_postHandlers[url] = handler;
}
6.1 添加全局头文件
#ifndef GLOBALHEAD_H
#define GLOBALHEAD_H#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/asio.hpp>#include <memory>#include <iostream>#include "Singletion.h"
#include <functional>
#include <map>
#include <unordered_map>
#include <string>#include <json/json.h>
#include <json/value.h>
#include <json/writer.h>
#include <json/reader.h>namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;enum ErrorCodes
{SUCCESS = 0,ERROR_JSON = 1001,RPC_FAILED = 1002,ERROR_JSON_KEY_EMAIL_LACK = 1003,
};#endif // GLOBALHEAD_H
6.2 绑定Post请求URL对应的回调函数
在这里需要Json来解析Post请求,首先获取Http请求中的内容,然后用jsonCpp这个第三方库的API去解析这个库,再设置服务器回送响应的内容,设置响应头为json文本,判断有没有解析成功,如果没解析成功向响应中添加没有解析成功的错误。如果解析成功了需要判断这个结果中是否包含有email,如果不包含向响应中添加Eamil确实的错误,最后设置解析成功的响应
// 注册Post请求URL对应的回调函数RegisterPost("/getVarifycode", [](HttpConnection* connection){if (connection){auto bodyStr = boost::beast::buffers_to_string(connection->_request.body()); // 获取 Http请求体中的内容cout << "receive body is " << bodyStr << endl;connection->_response.set(http::field::content_type, "text/json"); // 设置 Http响应头中的 content-typeJson::Value jsonResonse; // 响应用的JsonJson::Value jsonResult; // 请求体解析出来的JsonJson::Reader reader; // Json解析器bool parseSuccess = reader.parse(bodyStr, jsonResult); // 将请求体解析为Jsonif (!parseSuccess){cout << "parse json failed" << endl;jsonResonse["error"] = ErrorCodes::ERROR_JSON; // 设置响应的错误码string jsonStr = jsonResonse.toStyledString();beast::ostream(connection->_response.body()) << jsonStr; // 向 Http响应体中写入错误码内容return;}/* 解析成功了,判断是否有想要的key */if (!jsonResult.isMember("email")){cout << "email not found in json" << endl;jsonResonse["error"] = ErrorCodes::ERROR_JSON_KEY_EMAIL_LACK;string jsonStr = jsonResonse.toStyledString();beast::ostream(connection->_response.body()) << jsonStr;return;}auto email = jsonResonse["email"].asString(); // 获取解析出来的Emailcout << "email is " << email << endl;jsonResonse["error"] = 0;jsonResonse["email"] = jsonResult["email"];string jsonStr = jsonResonse.toStyledString();beast::ostream(connection->_response.body()) << jsonStr; // 向 Http响应体中写入Json内容return;}else{std::cout << "connection is null" << std::endl;}});
6.3 处理Post请求的函数
bool HandlePost(std::string, HttpConnection*); // 处理客户端Post请求
bool LogicSystem::HandlePost(std::string url, HttpConnection* connection)
{if (_postHandlers.find(url) == _postHandlers.end())return false;_postHandlers[url](connection);return true;
}
6.4 在连接处理客户端请求时添加Post的判断
// 如果是Http的Post请求if (_request.method() == http::verb::post) {bool IsSucceed = LogicSystem::GetInstance()->HandleGet(_request.target(), this);if (!IsSucceed){_response.result(http::status::not_found);_response.set(http::field::content_type, "text/plain");beast::ostream(_response.body()) << "url not found\r\n";WriteResponse();return;}_response.result(http::status::ok);_response.set(http::field::server, "GateServer");WriteResponse();return;}
7. 编译并测试
7.1 编译
参数不匹配,直接丢给AI,问正确的参数是什么?
更改为下列参数
编译成功
7.2 测试Post请求
下载PostMan,需要魔法哦
1. 创建空白分支
2. 填充要发送的客户端请求
URL没有被发现
离谱嗷,更改一下key为下面这一个
还是报错,又回头看了一下代码,复制的get请求,头晕眼花了忘记改了
将之前更改的Key再改回去,跑通了,上传Gitee