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日

相关文章

  • 关于java:从double转换为long 完全转换我的数字

    在Java中,将double类型的数字转换为long类型的数字可能会导致精度丢失。为了确保转换的准确性,可以使用Math.round()方法将double类型的数字舍五入为最接近的类型的数字。以下是将double类型的数字转换为long的数字的完整攻略,包括语法、示例和注意事项。 语法 在Java中,将double类型的数字转换为long类型的数字的语法如下…

    other 2023年5月7日
    00
  • Scala安装及环境图文配置教程

    本文将详细讲解Scala安装及环境配置的步骤和注意事项,以及如何进行图文配置教程。以下是具体操作步骤: 1.安装Java Scala是基于Java平台开发的语言,因此在安装Scala之前,需要先安装Java。 Windows环境 在Windows环境下安装Java的步骤如下: 打开Java官网,下载适合你操作系统的Java安装包; 双击下载的Java安装包,…

    other 2023年6月27日
    00
  • Python之关于类变量的两种赋值区别详解

    Python之关于类变量的两种赋值区别详解 在Python中,类变量是在类定义中声明的变量,它们是类的所有实例共享的属性。在给类变量赋值时,有两种不同的方式:直接在类定义中赋值和在类的方法中赋值。这两种方式有一些区别,下面将详细讲解它们的差异。 直接在类定义中赋值 当我们在类定义中直接给类变量赋值时,该变量将成为类的一个属性,所有的实例都可以访问和修改它。这…

    other 2023年8月9日
    00
  • IE8 兼容性问题(属性名区分大小写)

    IE8 兼容性问题(属性名区分大小写)攻略 问题描述 在开发网页时,使用IE8浏览器时可能会遇到兼容性问题,其中一个常见问题是属性名区分大小写。在其他现代浏览器中,属性名不区分大小写,但在IE8中,属性名是区分大小写的。这可能导致在IE8中无法正确识别和应用属性。 解决方案 为了解决IE8兼容性问题,我们可以采取以下步骤: 统一使用小写属性名:将所有属性名转…

    other 2023年8月18日
    00
  • MySQL中给自定义的字段查询结果添加排名的方法

    要在MySQL中给自定义的字段查询结果添加排名,可以使用MySQL提供的用户变量来实现。具体的步骤如下: 1.首先,需要先使用SELECT语句查询出需要添加排名的字段。例如,查询出某个表中的成绩字段。 SELECT score FROM student; 2.在SELECT语句中使用用户变量,同时将变量初始化为0。 SELECT score, (@rank …

    other 2023年6月25日
    00
  • CentOS6中rsync服务器的安装与配置

    以下是 CentOS6 中 rsync 服务器的安装与配置的完整攻略: 安装 rsync yum install rsync -y 配置 rsync 服务端 创建 rsync 用户,并设置密码: useradd rsync passwd rsync 创建需要同步的文件夹: mkdir /data mkdir /data/www 修改 /etc/rsyncd.…

    other 2023年6月27日
    00
  • 如何使用jmockit进行单元测试

    如何使用JMockit进行单元测试 简介 在软件开发过程中,单元测试是非常重要的一个环节。通过编写单元测试程序,可以保证软件的每个单元都能够正确工作,提高代码的质量和可维护性。在进行单元测试时,我们通常会使用Mock框架来模拟测试对象的依赖关系。JMockit就是一个优秀的Mock框架,它提供了丰富的API和灵活的使用方式,非常适合进行单元测试。 本文将介绍…

    其他 2023年3月28日
    00
  • Shell编程中的特殊变量之位置变量介绍

    Shell编程中的特殊变量之位置变量介绍 在Shell编程中,位置变量是一类特殊的变量,用于存储命令行参数或者脚本中的位置参数。这些变量可以帮助我们在脚本中获取和处理用户传递的参数。本攻略将详细介绍Shell编程中的位置变量,并提供两个示例说明。 位置变量列表 在Shell脚本中,位置变量以$1、$2、$3等形式表示,其中$1表示第一个位置参数,$2表示第二…

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