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;
else
y = 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-type
Json::Value jsonResonse; // 响应用的Json
Json::Value jsonResult; // 请求体解析出来的Json
Json::Reader reader; // Json解析器
bool parseSuccess = reader.parse(bodyStr, jsonResult); // 将请求体解析为Json
if (!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(); // 获取解析出来的Email
cout << "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