当我们在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技术站