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

yizhihongxing

当我们在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日

相关文章

  • ASP.Net全局变量的设置和读取方法

    ASP.Net全局变量的设置和读取方法攻略 在ASP.Net中,可以使用Session对象或Application对象来设置和读取全局变量。全局变量可以在整个应用程序中共享和访问。 使用Session对象设置和读取全局变量 Session对象用于在用户会话之间存储和检索数据。以下是设置和读取全局变量的步骤: 设置全局变量: // 在某个页面或事件中设置全局变…

    other 2023年7月29日
    00
  • 从零开始打造mock平台-核心篇

    以下是详细讲解“从零开始打造mock平台-核心篇的完整攻略,过程中至少包含两条示例说明”的标准Markdown格式文本: 从零开始打造mock平台-核心篇 Mock平台是一种用于模拟API接口数据的工具,可以帮助开发人员在开发过程中快速构建和测试API接口。本攻略将介绍从零开始打造mock平台的核心步骤和技术。 步骤一:选择Mock平台框架 可以选择以下几种…

    other 2023年5月10日
    00
  • cmdbuild部署教程

    以下是CMDBuild部署教程的完整攻略,包括两个示例说明。 1. 安装Java 在安装CMDBuild之前,需要先安装Java。可以按照以下步骤进行: 打开终端,输入以下命令,安装Java: bash sudo apt-get install openjdk-8-jdk 等待安装完成后,输入以下命令,验证Java是否安装成功: bash java -ver…

    other 2023年5月9日
    00
  • C++实现LeetCode(6.字型转换字符串)

    让我来为你详细讲解“C++实现LeetCode(6.字型转换字符串)”的完整攻略。 1. 题目描述 这道题目的具体描述如下:给你一个字符串 s 和一个整数 numRows,表示字型转换中行数。 请你设计一个算法,将字符串 s 进行字型转换,使其按照 zigzag 的顺序输出并返回新的字符串。例如,输入字符串为 “PAYPALISHIRING”,行数为 3 时…

    other 2023年6月20日
    00
  • 一篇文章学会两种将python打包成exe的方式

    本文将详细讲解两种将Python程序打包成exe可执行文件的方法。 方法一:使用pyinstaller pyinstaller是一种常用的将Python程序打包成exe文件的工具,它可以将Python程序的代码和资产打包成单个自立的可执行文件,并附带所使用的Python解释器和第三方库,大大方便了Python程序的分发和运行。下面将介绍如何使用pyinsta…

    other 2023年6月25日
    00
  • 值得收藏的五个种子搜索引擎&磁力搜索引擎

    种子搜索引擎和磁力搜索引擎是用于搜索和下载种子文件和磁力链接的工具。本文将介绍五个值得收藏的子搜索引擎和磁力搜索引擎,并提供两个示例说明。 1. BT Kitty BT Kitty是一个功能强大的子搜索引,可以搜索各种类型的种子文件和磁力链接。它的搜索结果非常准确,而且速度非常快。以下使用BT Kitty搜索影的示例: 打开BT Kitty网站(https:…

    other 2023年5月7日
    00
  • linux chroot命令详解

    Linux chroot命令详解攻略 概述 chroot是Linux系统中的一个非常有用的命令,用于创建一个新的根目录(root directory),从而使得当前进程和它的子进程只能在这个新的根目录下运行。这种操作通常被称为”Change Root”,适用于一些安全和资源隔离的场景,比如系统安全、容器技术等。 使用chroot命令可以快速创建一个根目录,然…

    other 2023年6月27日
    00
  • 电脑怎样自定义快捷键简单实现

    下面我将详细讲解一下“电脑怎样自定义快捷键简单实现”的完整攻略。 1. 什么是自定义快捷键 自定义快捷键是指在电脑上自行设置的键盘快捷键,可以方便地进行一些常用操作,提高工作效率。 2. 如何自定义快捷键 2.1 Windows系统自定义快捷键 2.1.1 通过快捷方式设置 找到需要自定义快捷键的应用程序,创建快捷方式到桌面上。 右击快捷方式,选择“属性”,…

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