Seesion在C++服务端的使用方法

当我们在C++服务端开发过程中需要保持客户端的状态时,就可以使用Session来传递信息。Session可以存储客户端的信息并维持其状态,在服务端得到持续的处理。下面我们来介绍一下Session在C++服务端的使用方法,包含以下几个步骤:

1.创建Session

在HTTP请求处理过程中,我们需要首先创建Session对象来存储会话信息。一般情况下,我们会将Session对象与客户端的连接对象一一对应起来,比如用socket描述符来判别不同连接。

在C++服务端中,我们可以用第三方库boost来轻松创建Session。创建Session的代码如下:

// 在头文件中引入所需的库
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/connect.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/beast/core.hpp>

// 创建Session函数
std::string createSession() {
    // 生成UUID
    boost::uuids::uuid sessionId = boost::uuids::random_generator()();
    // 转换UUID类型
    std::string sessionIdString = boost::lexical_cast<std::string>(sessionId);
    return sessionIdString;
}

2.存储Session

Session一旦创建,我们需要将其存储在服务器端的缓存中。存储Session的方式,可以是将其以key-value的形式存储在内存中的缓存中,也可以是以文件的形式存储在磁盘中。

以Redis为例,我们可以通过使用Redis的C++客户端程序来将Session存储在Redis中。具体代码如下:

std::string sessionIdString = createSession();
// 创建连接
redispp::connection conn;
conn.connect("localhost", 6379);
// 存储Session,这里以key-value的形式存储在Redis中
redispp::command("SET")(sessionIdString)(sessionData).exec(conn);

其中sessionData是包含会话信息的变量,可以是JSON或其他格式。

3.获取Session

客户端在后续的请求中需要使用之前保存的Session对象,服务端需要根据客户端提交的Session ID来获取Session对象。具体获取Session的方式可以是从内存、文件或Redis等存储介质中读取。

我们可以采用如下的方式获取Session:

std::string sessionId = clientSessionId; // 获取客户端传来的Session ID
redispp::connection conn; // 创建Redis连接
conn.connect("localhost", 6379); // Redis服务地址和端口
redispp::reply reply = redispp::command("GET")(sessionId).exec(conn); // 从Redis中获取Session数据
std::string sessionData = reply.str(); // 将Session数据存储为string类型

示例

下面给出一个使用Session进行身份认证的示例:

示例1:HTTP服务端

在这个示例中,我们使用了boost.beast库。使用的是GET请求,获取响应需要借助handle的回调函数进行处理。

#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

std::string createSession();
std::string getSessionData(std::string sessionId);
void createSessionEntry(std::string sessionId, std::string data);

int main(int argc, char* argv[]) {
    boost::asio::io_context ioc;
    std::string address = "127.0.0.1";
    auto const port = static_cast<unsigned short>(std::stoi("8080"));
    auto endpoint = boost::asio::ip::tcp::endpoint{boost::asio::ip::make_address(address), port};

    boost::asio::ip::tcp::acceptor acceptor{ioc, endpoint};

    for (;;) {
        boost::asio::ip::tcp::socket socket{ioc};
        // accept函数会阻塞,等待客户端连接
        acceptor.accept(socket);
        boost::system::error_code ec;
        boost::beast::flat_buffer buffer;
        boost::beast::http::request<boost::beast::http::string_body> request;
        boost::beast::http::response<boost::beast::http::string_body> response;

        // 接收请求数据
        boost::beast::http::read(socket, buffer, request, ec);

        // 处理请求
        if (ec == boost::beast::http::error::end_of_stream) {
            break;
        } else if (ec) {
            std::cerr << "接收请求出错: " << ec.message() << "\n";
            break;
        }

        std::string action = request.target().to_string();

        // 验证Session
        if (action == "/todo") {
            if (request.base().find("Cookie") == request.base().end()) {
                // 若未收到Session,则跳转至登录页面
                response.result(boost::beast::http::status::temporary_redirect);
                std::string location = "http://" + address + ":" + std::to_string(port) + "/login";
                response.set(boost::beast::http::field::location, location.c_str());
                boost::beast::http::write(socket, response);
                continue;
            } else {
                auto cookie = request.base()["Cookie"].to_string();
                std::string sessionId = cookie.substr(cookie.find("=") + 1, cookie.length() - 1);

                std::string sessionData = getSessionData(sessionId); // 从Redis中读取Session数据

                // 若未找到Session,跳转至登录页面
                if (sessionData.empty()) {
                    response.result(boost::beast::http::status::temporary_redirect);
                    std::string location = "http://" + address + ":" + std::to_string(port) + "/login";
                    response.set(boost::beast::http::field::location, location.c_str());
                    boost::beast::http::write(socket, response);
                    continue;
                }
                // 根据Session验证登录状态
                if (sessionData != "loggedIn") {
                    response.result(boost::beast::http::status::temporary_redirect);
                    std::string location = "http://" + address + ":" + std::to_string(port) + "/login";
                    response.set(boost::beast::http::field::location, location.c_str());
                    boost::beast::http::write(socket, response);
                    continue;
                }

                // 设置响应
                response.result(boost::beast::http::status::ok);
                response.set(boost::beast::http::field::content_type, "text/html");
                response.body() = "Todo list";
                response.prepare_payload();
            }
        } else if (action == "/login") {
            // 获取表单数据
            auto body = request.body();
            std::string username, password;
            std::string& data = body;
            std::size_t pos = data.find("username=");
            if (pos == std::string::npos) {
                std::cerr << "未找到用户名\n";
                continue;
            } else {
                username = data.substr(pos + 9, data.find("&") - pos - 9);
            }
            pos = data.find("password=");
            if (pos == std::string::npos) {
                std::cerr << "未找到密码\n";
                continue;
            } else {
                password = data.substr(pos + 9, data.length() - pos - 9 - 1);
            }

            // 根据账号信息验证登录状态
            if (username == "admin" && password == "admin") {
                std::string sessionId = createSession(); // 创建Session
                createSessionEntry(sessionId, "loggedIn"); // 存储Session到Redis中

                // 设置响应和Cookie
                response.result(boost::beast::http::status::found);
                std::string location = "http://" + address + ":" + std::to_string(port) + "/todo";
                response.set(boost::beast::http::field::location, location.c_str());
                response.set(boost::beast::http::field::set_cookie, "sessionId=" + sessionId + "; Path=/");
                boost::beast::http::write(socket, response);
                continue;
            } else {
                // 设置错误响应并跳转至登录页面
                response.result(boost::beast::http::status::bad_request);
                response.set(boost::beast::http::field::content_type, "text/html");
                response.body() = "Incorrect username or password";
                response.prepare_payload();
            }
        } else {
            // 设置404响应
            response.result(boost::beast::http::status::not_found);
            response.set(boost::beast::http::field::content_type, "text/html");
            response.body() = "Page not found";
            response.prepare_payload();
        }

        // 发送响应数据
        boost::beast::http::write(socket, response);
    }

    return 0;
}

// 创建Session
std::string createSession() {
    boost::uuids::uuid sessionId = boost::uuids::random_generator()();
    std::string sessionIdString = boost::lexical_cast<std::string>(sessionId);
    return sessionIdString;
}

// 根据Session ID从Redis中获取Session数据
std::string getSessionData(std::string sessionId) {
    redispp::connection conn;
    conn.connect("localhost", 6379);
    redispp::reply reply = redispp::command("GET")(sessionId).exec(conn);
    std::string sessionData = reply.str();
    return sessionData;
}

// 存储Session到Redis中
void createSessionEntry(std::string sessionId, std::string data) {
    redispp::connection conn;
    conn.connect("localhost", 6379);
    redispp::command("SET")(sessionId)(data).exec(conn);
}

示例2:WebSocket服务端

在这个示例中,我们使用了boost.beast库。WebSocket是一种协议,它可以实现双向实时通信。WebSocket可以完全控制运输层协议,因此不受HTTP协议的限制,可以传送任意格式的数据。

本例中,我们使用WebSocket实现了简单的聊天室功能,支持在线发送消息和查看消息。

#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/version.hpp>
#include <boost/beast/websocket.hpp>

#include <iostream>
#include <string>
#include <map>
#include <memory>

std::string createSession();
std::string getSessionData(std::string sessionId);
void createSessionEntry(std::string sessionId, std::string data);

class session {
public:
    boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws;
    std::string sessionId;

    session(boost::asio::ip::tcp::socket&& socket)
        : ws(std::move(socket))
        , sessionId(createSession()) {
        ws.set_option(boost::beast::websocket::stream_base::timeout::suggested(boost::beast::role_type::server));
        ws.set_option(boost::beast::websocket::stream_base::decorator([](boost::beast::websocket::response_type& res) {
            res.set(boost::beast::http::field::server,"Beast");
        }));
    }

    ~session() {
        saveSessionData("{\"status\":\"offline\"}");
    }

    void run() {
        auto self = shared_from_this();
        ws.async_accept([self](boost::system::error_code ec) {
            if (ec) {
                return;
            }
            self->on_accept();
        });
    }

    void on_accept() {
        auto self = shared_from_this();
        read();
    }

private:
    void read() {
        auto self = shared_from_this();
        ws.async_read(buffer, [self](boost::system::error_code ec, std::size_t bytes_transferred) {
            if (ec) {
                return;
            }
            self->on_read(bytes_transferred);
        });
    }

    void on_read(std::size_t bytes_transferred) {
        std::string message(boost::beast::buffers_to_string(buffer.data()));
        buffer.consume(bytes_transferred);
        processMessage(message);
        read();
    }

    void send(std::string message) {
        boost::asio::post(ws.get_executor(), [self = shared_from_this(), message] {
            self->ws.text(true);
            self->ws.write(boost::asio::buffer(message));
        });
    }

    void processMessage(std::string message) {
        auto json = nlohmann::json::parse(message);
        std::string type = json["type"].get<std::string>();

        if (type == "name") {
            std::string name = json["name"].get<std::string>();
            saveSessionData(name);
        } else if (type == "message") {
            std::string data = "{\"username\":\"" + getSessionData(sessionId) + "\",\"message\":\"" + json["message"].get<std::string>() + "\"}";
            for (auto& p : sessions) {
                if (p.first != sessionId) {
                    p.second->send(data);
                }
            }
        }
    }

    // 存储Session到Redis中
    void saveSessionData(std::string data) {
        createSessionEntry(sessionId, data);
    }

    boost::beast::flat_buffer buffer;
};

std::string createSession() {
    boost::uuids::uuid sessionId = boost::uuids::random_generator()();
    std::string sessionIdString = boost::lexical_cast<std::string>(sessionId);
    return sessionIdString;
}

std::string getSessionData(std::string sessionId) {
    redispp::connection conn;
    conn.connect("localhost", 6379);
    redispp::reply reply = redispp::command("GET")(sessionId).exec(conn);
    std::string sessionData = reply.str();
    if (sessionData.empty()) {
        return "";
    }
    return nlohmann::json::parse(sessionData)["name"];
}

void createSessionEntry(std::string sessionId, std::string data) {
    redispp::connection conn;
    conn.connect("localhost", 6379);
    redispp::command("SET")(sessionId)(data).exec(conn);
}

std::map<std::string, std::shared_ptr<session>> sessions;

int main(int argc, char* argv[]) {
    boost::asio::io_context ioc;
    std::string address = "0.0.0.0";
    auto const port = static_cast<unsigned short>(std::stoi("8080"));
    auto endpoint = boost::asio::ip::tcp::endpoint{boost::asio::ip::make_address(address), port};
    try {
        boost::asio::ip::tcp::acceptor acceptor{ioc, endpoint};
        for (;;) {
            boost::asio::ip::tcp::socket socket{ioc};
            acceptor.accept(socket);
            auto sessionPtr = std::make_shared<session>(std::move(socket));
            sessionPtr->run();
            sessions.insert(std::pair<std::string, std::shared_ptr<session>>(sessionPtr->sessionId, sessionPtr));
            std::cout << "New client connected: " << sessionPtr->sessionId << "\n";
        }
    } catch (std::exception& e) {
        std::cerr << "Error: " << e.what() << "\n";
    }
    return 0;
}

以上就是使用Session在C++服务端的使用方法的详细攻略。在实践中,我们可以根据需求进行相应的改进和优化。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Seesion在C++服务端的使用方法 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Win11提示0x800704cf错误怎么办? Win11不能访问网络位置的解决方法

    Win11提示0x800704cf错误怎么办? 在 Win11 操作系统中,有用户反馈遭遇到了“Win11提示0x800704cf错误”的问题。这个错误表示操作系统在尝试访问网络位置时遇到了问题。下面是解决此问题的步骤。 步骤1:检查网络设置 首先要检查的是计算机的网络设置。要确保网络设置正确,以允许计算机访问 Internet。以下是详细步骤。 1.1 打…

    other 2023年6月27日
    00
  • mysql5.7 修改用户初始密码的方法

    下面是mysql5.7修改用户初始密码的方法的完整攻略: 1. 登录MySQL 在修改用户初始密码之前,需要先登录到MySQL中。可以使用以下命令登录到MySQL: mysql -u用户名 -p密码 这里需要将“用户名”和“密码”替换为正确的登录信息。 2. 修改用户密码 MySQL 5.7 之后推荐使用 ALTER USER 命令来修改用户密码。操作方式如…

    other 2023年6月20日
    00
  • windows8系统账号自动登录默认设置2种方式

    Windows 8系统支持两种方式设置自动登录:本地计算机账号自动登录和Microsoft账号自动登录。下面分别详细讲解这两种方式的设置步骤。 本地计算机账号自动登录 打开“运行”对话框,方法:按下“Win + R”组合键,或者在开始菜单中搜索“运行”。 输入“netplwiz”命令并点击“确定”按钮。 在“用户账户”窗口中,取消勾选“要使用本计算机,用户必…

    other 2023年6月27日
    00
  • React props和state属性的具体使用方法

    下面是React props和state属性的具体使用方法的完整攻略。 什么是React props和state属性 React是一种基于组件的JavaScript库,因此props和state是React的两大重要概念。 props是properties的缩写,指的是组件属性。它是由外部组件传递给组件的数据,类似于函数参数。可以让控件可配置、可复用。 st…

    other 2023年6月27日
    00
  • Javascript变量函数声明提升深刻理解

    Javascript变量函数声明提升是JavaScript的一个重要特性,了解它可以帮助我们更加深入地理解JavaScript的工作原理。本篇攻略将逐步介绍JavaScript变量和函数声明提升的概念、原理和实现。 一、变量声明提升 在JavaScript中,变量可以通过关键字var,let和const来声明。其中,使用var关键字声明的变量具有变量声明提升…

    other 2023年6月27日
    00
  • IIS7无法读取配置文件解决办法

    针对“IIS7无法读取配置文件解决办法”这个问题,我们需要采取以下几个步骤来解决。 1. 检查文件权限 首先要检查的是配置文件的权限,因为在IIS7中,如果配置文件的权限设置不正确,就会导致无法读取配置文件。可以按照以下步骤进行检查: 找到配置文件所在的目录,在目录上右键单击,选择“属性”选项。 在弹出的窗口中,选择“安全”选项卡,检查是否有“IIS_IUS…

    other 2023年6月25日
    00
  • linux’nospaceleftondevice’磁盘空间解决办法

    Linux ‘nospaceleftondevice’磁盘空间解决办法 当你使用Linux时,你可能会遇到“nospaceleftondevice”的错误。这个错误表示你的硬盘空间已经满了,Linux不能再往硬盘中写入数据了。在这篇文章中,我们将为你提供几种可能的解决方案。 1. 查看硬盘空间 首先,我们需要查看当前硬盘的使用情况。我们可以使用以下命令来查看…

    其他 2023年3月28日
    00
  • 详述Windows Server 2008安全部署的六个方面

    详述Windows Server 2008安全部署的六个方面: 服务器硬件和操作系统的安全性 在安装Windows Server 2008之前,需要检查服务器硬件的物理安全性。服务器的物理访问权限必须受到保护,确保没有任何未经授权的人员能够访问服务器。此外,在安装和配置操作系统时,需要采取一系列措施来保护服务器的安全性。这些措施包括设置强密码、启用防火墙、关…

    other 2023年6月27日
    00
合作推广
合作推广
分享本页
返回顶部