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;
    }
}

posted @ 2025-12-24 23:16  大胖熊哈  阅读(2)  评论(0)    收藏  举报