Chap03-GateWayServer
Chap03-GateWayServer
网关服务器
GateWayServer在分布式系统中扮演者流量入口和请求调度的关键角色.
在我们的项目分布式系统中,主要是扮演入口的角色,用来进行注册,验证码,登陆等操作.
首先同上一节的要先实现单例类singleton
#ifndef SINGLETON_H
#define SINGLETON_H
#include <memory>
template <typename T>
class Singleton {
protected:
Singleton() = default;
~Singleton() = default;
public:
Singleton(const Singleton<T>&) = delete;
Singleton& operator=(const Singleton<T>&) = delete;
static std::shared_ptr<T> GetInstance()
{
static std::shared_ptr<T> _instance = std::shared_ptr<T>(new T);
return _instance;
}
};
#endif
再创建GateWayServer类,用于接受连接,分发Session.
//.h
#ifndef GATEWAYSERVER_H
#define GATEWAYSERVER_H
#include "const.h"
#include <memory>
class GateWayServer : public std::enable_shared_from_this<GateWayServer> {
public:
GateWayServer(net::io_context& ioc, unsigned short port);
void Start();
private:
net::ip::tcp::acceptor _acceptor;
net::io_context& _ioc;
boost::asio::ip::tcp::socket _socket;
};
#endif
//.cpp
#include "GateWayServer.h"
#include "Session.h"
#include <iostream>
GateWayServer::GateWayServer(net::io_context& ioc, unsigned short port)
: _ioc(ioc)
, _acceptor(ioc, net::ip::tcp::endpoint(net::ip::tcp::v4(), port))
, _socket(ioc)
{
}
void GateWayServer::Start()
{
_acceptor.async_accept(_socket, [this, self = shared_from_this()](const boost::system::error_code& ec) {
try {
if (ec) {
self->Start();
return;
}
std::make_shared<Session>(std::move(self->_socket))->Start();
self->Start();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
});
}
一个Session,对应一个连接的实例,相当于两个qq好友聊天窗口,互通信息.
这个Session首先async_read异步的读对方发来的http请求,如果有请求过来,那么就使用HandleRequest去处理,同时开启定时器,一定时间内对方无发送信息,就断开连接.
在HandleRequest中,我们把(暂时)请求分为了post和get请求,每个请求通过LogicSystem上层统一处理.如果处理无异常,就设置回应头(HandleHeaders),然后把回应发送回去(WriteResponse).
我们可以注意到下面的代码,还专门写了UrlEncode和UrlDecode.主要是get请求的时候,参事可能是中文等一些常见字符,会导致乱码,因此我们需要对中文等字符进行特殊处理.比如在浏览器输入中文的时候,浏览器会自动处理中文,方式如下:
utf-8默认一个中文占3个字节.但是我们处理的时候是按照字节处理成两个16进制位.因此,每个中文会被处理成三个16进制.为了表示每个字节,我们在每个字节前面加一个%.处理每一个字节的时候,高四位为一个16进制位,然后低四位作于第二个16进制位.对应代码中的这里:
temp += '%';
temp += ToHex((unsigned char)str[i] >> 4);
temp += ToHex((unsigned char)str[i] & 0x0F);
举个例子,比如你好的'你',共三个字节:11100100 10111101 10100000
字节1: 11100100 = 0xE4
字节2: 10111101 = 0xBD
字节3: 10100000 = 0xA0
因此 '你' --> "%E4%BD%A0".
同理解码的时候,只需要倒回去即可.每一个字节解析出第一个16进制*16再加上第二个16进制位,则是原来的一个字节的值.
要注意,测试的时候一定要在头中设置charset=utf-8,要不然仍会有乱码问题.
_response.set(http::field::content_type, type + ";charset=utf-8");
//.h
#ifndef SESSION_H
#define SESSION_H
#include "const.h"
#include <memory>
class Session : public std::enable_shared_from_this<Session> {
friend class LogicSystem;
public:
Session(net::ip::tcp::socket socket);
void Start();
private:
void CheckDeadLine();
void DeadlineCancel();
void WriteResponse();
void ParseGetParam();
void HandleRequest();
void HandleHeaders(bool success, const std::string& type = "text/plain", const std::string& body = "");
private:
// 套接字
net::ip::tcp::socket _socket;
// 定时器
net::steady_timer _deadlineTimer { _socket.get_executor(), std::chrono::seconds(30) };
// 缓冲区
beast::flat_buffer _buffer { 8192 };
// 请求
http::request<http::dynamic_body> _request;
// 响应
http::response<http::dynamic_body> _response;
std::string _get_url;
std::unordered_map<std::string, std::string> _get_params;
};
#endif
//.cpp
#include "Session.h"
#include "LogicSystem.h"
#include <iostream>
// 10进制转16进制(字符串形式)
unsigned char ToHex(int x)
{
return x > 9 ? x + 55 : x + 48;
}
// 16进制字符串转10进制
unsigned char FromHex(unsigned char x)
{
unsigned char y;
if (x >= '0' && x <= '9')
y = x - '0';
else if (x >= 'a' && x <= 'f')
y = x - 'a' + 10;
else if (x >= 'A' && x <= 'F')
y = x - 'A' + 10;
else
assert(0);
return y;
}
// 编码
std::string UrlEncode(const std::string& str)
{
std::string temp = "";
std::size_t length = str.size();
for (std::size_t i = 0; i < length; ++i) {
if (std::isalnum(static_cast<unsigned char>(str[i])) || str[i] == '=' || (str[i] == '-') || (str[i] == '_') || (str[i] == '.') || (str[i] == '~'))
temp += str[i];
else if (str[i] == ' ') {
temp += '+';
} else {
temp += '%';
temp += ToHex((unsigned char)str[i] >> 4);
temp += ToHex((unsigned char)str[i] & 0x0F);
}
}
return temp;
}
// 解析
std::string UrlDecode(const std::string& str)
{
std::string temp = "";
for (std::size_t i = 0; i < str.size(); ++i) {
if (str[i] == '+')
temp += ' ';
else if (str[i] == '%') {
assert(i + 2 < str.size());
unsigned char high = FromHex(str[++i]);
unsigned char low = FromHex(str[++i]);
temp += ((high << 4) + low);
} else
temp += str[i];
}
return temp;
}
Session::Session(net::ip::tcp::socket socket)
: _socket(std::move(socket))
{
}
void Session::Start()
{
http::async_read(_socket, _buffer, _request, [self = shared_from_this()](const boost::system::error_code& ec, std::size_t bytes_transferred) {
try {
if (ec) {
std::cerr << "Error reading request: " << ec.message() << std::endl;
return;
}
boost::ignore_unused(bytes_transferred);
self->HandleRequest();
self->CheckDeadLine();
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
});
}
void Session::CheckDeadLine()
{
auto self = shared_from_this();
_deadlineTimer.async_wait([self](beast::error_code ec) {
ec = self->_socket.close(ec);
});
}
void Session::DeadlineCancel()
{
_deadlineTimer.cancel();
}
void Session::WriteResponse()
{
http::async_write(_socket, _response, [self = shared_from_this()](boost::system::error_code ec, std::size_t bytes_transferred) {
ec = self->_socket.shutdown(net::ip::tcp::socket::shutdown_send, ec);
self->DeadlineCancel();
});
}
void Session::ParseGetParam()
{
auto url = _request.target();
auto query_pos = url.find('?');
if (query_pos == std::string::npos) {
_get_url = url;
return;
}
_get_url = url.substr(0, query_pos);
std::string query_string = url.substr(query_pos + 1);
std::string key;
std::string value;
std::size_t pos;
while ((pos = query_string.find('&')) != std::string::npos) {
auto pair = query_string.substr(0, pos);
std::size_t equal_pos = pair.find('=');
if (equal_pos != std::string::npos) {
key = UrlDecode(pair.substr(0, equal_pos));
value = UrlDecode(pair.substr(equal_pos + 1));
_get_params[key] = value;
}
query_string.erase(0, pos + 1);
}
if (!query_string.empty()) {
std::size_t equal_pos = query_string.find('=');
if (equal_pos != std::string::npos) {
key = UrlDecode(query_string.substr(0, equal_pos));
value = UrlDecode(query_string.substr(equal_pos + 1));
_get_params[key] = value;
}
}
}
void Session::HandleRequest()
{
_response.version(_request.version());
_response.keep_alive(false);
if (_request.method() == http::verb::get) {
ParseGetParam();
bool success = LogicSystem::GetInstance()->HandleGet(_get_url, shared_from_this());
if (!success) {
HandleHeaders(false);
} else {
HandleHeaders(true);
}
WriteResponse();
} else if (_request.method() == http::verb::post) {
bool success = LogicSystem::GetInstance()->HandlePost(_request.target(), shared_from_this());
if (!success) {
HandleHeaders(false);
} else {
HandleHeaders(true);
}
WriteResponse();
}
}
void Session::HandleHeaders(bool success, const std::string& type, const std::string& body)
{
_response.set(http::field::content_type, type + ";charset=utf-8");
if (!success) {
_response.result(http::status::not_found);
beast::ostream(_response.body()) << "Not found";
} else {
_response.result(http::status::ok);
}
}
LogicSystem是作为上层统一处理消息的类.首先开始注册处理对应路由的方法,放入到对应的unordered_map中.然后由连接实力Session调用对应的post/get方法.如果在unordered_map池子中找到了对应的方法,就解决,否则就返回错误代码.
// .h
#ifndef LOGICSYSTEM_H
#define LOGICSYSTEM_H
#include "Singleton.h"
#include "const.h"
#include <unordered_map>
class Session;
using SessionHandler = std::function<void(std::shared_ptr<Session>)>;
class LogicSystem : public Singleton<LogicSystem>, public std::enable_shared_from_this<LogicSystem> {
friend class Singleton<LogicSystem>;
public:
~LogicSystem() = default;
bool HandleGet(const std::string&, std::shared_ptr<Session>);
bool HandlePost(const std::string&, std::shared_ptr<Session>);
void RegistHandlers(const std::string&, RequestType type, SessionHandler);
private:
LogicSystem();
private:
std::unordered_map<std::string, SessionHandler> _post_handlers;
std::unordered_map<std::string, SessionHandler> _get_handlers;
};
#endif // LOGICSYSTEM_H
//.cpp
#include "LogicSystem.h"
#include "Session.h"
#include <iostream>
#include <regex>
LogicSystem::LogicSystem()
{
RegistHandlers("/get_test", RequestType::GET, [this](std::shared_ptr<Session> session) {
beast::ostream(session->_response.body()) << "receive login request\n";
int i = 0;
for(const auto&ele:session->_get_params ){
i++;
beast::ostream(session->_response.body()) << i <<"\t" << ele.first << ":" << ele.second << "\n";
} });
RegistHandlers("/getSecurityCode", RequestType::POST, [this](std::shared_ptr<Session> session) {
auto body_str = beast::buffers_to_string(session->_request.body().data());
std::cout << "receive getSecurityCode request, body: " << body_str << std::endl;
session->_response.set(http::field::content_type, "text/json");
// json解析
json j = json::parse(body_str);
if (j.is_discarded()) {
std::cerr << "无效json" << std::endl;
j["error"] = ErrorCodes::ERROR_JSON;
std::string returnJson = j.dump(4);
beast::ostream(session->_response.body()) << returnJson;
return true;
}
auto sendError = [&](ErrorCodes error_code, const std::string& message) {
json error_response = {
{ "success", false },
{ "error", error_code },
{ "message", message }
};
beast::ostream(session->_response.body()) << error_response.dump(4);
return true; // 返回true表示请求处理完成
};
std::cout << "parse json: " << j.dump() << std::endl;
if (!j.contains("email")) {
return sendError(ErrorCodes::RPCFAILED, "email is required");
}
auto email = j["email"].get<std::string>();
std::cout << email << std::endl;
// 验证格式
std::regex email_regex(R"(\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b)");
if (!std::regex_match(email, email_regex)) {
return sendError(ErrorCodes::RPCFAILED, "email format error");
}
// 发送验证码
json returnJson = {
{ "success", true },
{ "error", ErrorCodes::SUCCESS },
{ "message", "okle!" }
};
beast::ostream(session->_response.body()) << returnJson.dump(4);
return true;
});
}
bool LogicSystem::HandleGet(const std::string& route, std::shared_ptr<Session> handler_ptr)
{
if (_get_handlers.find(route) == _get_handlers.end()) {
return false;
}
_get_handlers[route](handler_ptr);
return true;
}
bool LogicSystem::HandlePost(const std::string& route, std::shared_ptr<Session> handler_ptr)
{
if (_post_handlers.find(route) == _post_handlers.end()) {
return false;
}
_post_handlers[route](handler_ptr);
return true;
}
void LogicSystem::RegistHandlers(const std::string& route, RequestType type, SessionHandler handler)
{
type == RequestType::GET ? _get_handlers[route] = handler : _post_handlers[route] = handler;
}
main.cp仅仅是启动GateWayServer,等待GateWayServer接受连接.
#include "GateWayServer.h"
#include <boost/asio.hpp>
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main(int, char**)
{
try {
unsigned short port = 9999;
net::io_context ioc;
net::signal_set signals(ioc, SIGINT, SIGTERM);
signals.async_wait([&ioc](const boost::system::error_code&, int) { ioc.stop(); });
std::make_shared<GateWayServer>(ioc, port)->Start();
ioc.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 1;
}
}

浙公网安备 33010602011771号