当前位置: 首页 > ds >正文

HTTPcookie与session实现

1.HTTP Cookie

定义

HTTP Cookie (也称为 Web Cookie 、浏览器 Cookie 或简称 Cookie )是服务器发送到
用户浏览器并保存在浏览器上的一小块数据,它会在浏览器之后向同一服务器再次发
起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一
浏览器,如保持用户的登录状态、记录用户偏好等。
工作原理
用户第一次访问网站时,服务器会在响应的HTTP头中设置Set-Cookie字段,发送Cookie到用户的浏览器上,浏览器接收到cookie后,会保存到本地,之后的请求中,浏览器会自动在HTTP请求头中携带Cookie字段,将之前保存的Cookie信息发送到服务器。
分类
会话Cookie(Session Cookie):在浏览器关闭时失效
持久Cookie(Persistent Cookie):带有明确的过期日期或者持续时间,可以跨多个浏览器会话存在。
如果是一个持久的cookie,其实就是浏览器相关的,特定目录下的一个文件,但是无法直接查看文件,因为cookie文件一般时以二进制或者sqlite格式存储,查看就需要到浏览器对应的cookie中查看。

认识cookie

HTTP存在一个报头选项:Set-cookie,可以用来进行给浏览器设置cookie值。

在HTTP响应头中添加,客服端获取并自行设置并保存。

完整的Set-Cookie实例

C++
Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00
UTC; path=/; domain=.example.com; secure; HttpOnly
时间格式必须遵守 RFC 1123 标准,具体格式样例: Tue, 01 Jan 2030 12:34:56
GMT 或者 UTC( 推荐 )
关于时间解释
Tue : 星期二(星期几的缩写)
, : 逗号分隔符
01 : 日期(两位数表示)
Jan : 一月(月份的缩写)
2030 : 年份(四位数)
12:34:56 : 时间(小时、分钟、秒)
GMT : 格林威治标准时间(时区缩写)
区别:
计算方式: GMT 基于地球的自转和公转,而 UTC 基于原子钟。
准确度:由于 UTC 基于原子钟,它比基于地球自转的 GMT 更加精确。
在实际使用中, GMT UTC 之间的差别通常很小,大多数情况下可以互换使用。但
在需要高精度时间计量的场合,如科学研究、网络通信等, UTC 是更为准确的选择。
关于其他可选属性的解释
expires=<date> [ 要验证 ] :设置 Cookie 的过期日期 / 时间。如果未指定此属
性,则 Cookie 默认为会话 Cookie ,即当浏览器关闭时过期。
path=<some_path> [ 要验证 ] :限制 Cookie 发送到服务器的哪些路径。默认
为设置它的路径。
domain=<domain_name> [ 了解即可 ] :指定哪些主机可以接受该 Cookie 。默
认为设置它的主机。
secure [ 了解即可 ] :仅当使用 HTTPS 协议时才发送 Cookie 。这有助于防止
Cookie 在不安全的 HTTP 连接中被截获。
HttpOnly [ 了解即可 ] :标记 Cookie HttpOnly ,意味着该 Cookie 不能被
客户端脚本(如 JavaScript )访问。这有助于防止跨站脚本攻击( XSS )。

2.HTTP Session

定义
HTTP Session 是服务器用来跟踪用户与服务器交互期间用户状态的机制。由于 HTTP
协议是无状态的(每个请求都是独立的),因此服务器需要通过 Session 来记住用户
的信息。(无状态是无法记录历史访问)
工作原理
用户首次访问网站时,服务器会为用户创建一个唯一的Session ID,通过Cookie将其发送到客服端。客服端在之后的请求都会携带这个Session ID,服务区通过Session ID来标识用户,从而获取用户的会话信息。服务器通常会将Session信息存储在内存,数据库或缓存中。
安全性:
Cookie 相似,由于 Session ID 是在客户端和服务器之间传递的,因此也存 在被窃取的风险。
但是一般虽然 Cookie 被盗取了,但是用户只泄漏了一个 Session ID ,私密信息暂时没有被泄露的风险。Session ID 便于服务端进行客户端有效性的管理,比如异地登录。 可以通过 HTTPS 和设置合适的 Cookie 属性(如 HttpOnly Secure )来增强安全性。
没有session的风险

 没有Session时的隐私风险

如果没有Session机制,Web应用可能需要依赖其他方式(如Cookie)来跟踪用户状态。Cookie存储在客户端,容易被恶意程序获取和篡改,从而导致隐私泄露。此外,没有Session的加密通信工具可能无法提供端到端加密或去中心化存储,从而增加数据被窃取或监控的风险。

有session好处
  • 数据存储位置:Session数据存储在服务器端,而不是客户端。即使客户端的Session ID被窃取,攻击者也无法直接获取到存储在服务器上的会话数据。

  • 安全性:服务器可以更好地控制Session的生命周期,例如定期更新Session ID、限制Session的使用范围等,从而降低被攻击的风险。

补充
favicon.ico是一个网站图标,通常显示浏览器的标签页上,地址栏旁边或者收藏夹中。

3.cookie实现

HttpProtocol.hpp文件

第一个类是实现网络请求的,GetLine函数用来截取一行,以HttpSep(\r\n)来作为分隔符,则0到pos位置就是一行的内容了,把读取到的内容在删除掉,读取下一行内容。Deserialize函数是序列化,request是网络请求,用getline函数获取一行出来,第一行是请求行,单独存储起来,然后就是一直死循环读取剩下的报头数据,直到读到了空行,如果是空行的话ok为true且empty也会true则就会把剩下的内容全都放到req_content里,这里就是文本内容了,也是要单独存储的,empty不为空就说明还是在读取报头信息,就把报头信息放到vector里面存储,这里是把键和值一起放到string里面的。

#pragma once#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include "TcpServer.hpp"const std::string HttpSep = "\r\n";
// 可以配置的
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";class HttpRequest
{
public:HttpRequest():_req_blank(HttpSep),_path(wwwroot){}bool GetLine(std::string& str,std::string* line){auto pos=str.find(HttpSep);if(pos==std::string::npos)return false;*line=str.substr(0,pos);str.erase(0,pos+HttpSep.size());return true;}bool Deserialize(std::string& request){std::string line;bool ok=GetLine(request,&line);if(!ok)return false;_req_line=line;while(true){bool ok=GetLine(request,&line);if(ok&&line.empty()){_req_content=request;break;}else if(ok&&!line.empty()){_req_header.push_back(line);}elsebreak;}return true;}void DebugHttp(){std::cout<<"_req_line:"<<_req_line<<std::endl;for(auto& line:_req_header){std::cout<<"--->"<<line<<std::endl;}}~HttpRequest(){}
private:std::string _req_line;std::vector<std::string> _req_header;std::string _req_blank;std::string _req_content;std::string _method;std::string _url;std::string _http_version;std::string _path;std::string _suffix;
};

 第二个代码是网络响应的,构造函数设置网络版本和状态码和状态信息。SetCode函数设置状态码的值,SetDesc函数设置状态信息,MakeStatusLine函数构造状态行,网络版本+空格号+状态码+空格号+状态信息+换行拼接在一起。AddHeader函数把报头信息+换行符插入到vector中。AddContent函数设置文内容。Serialize函数先构造出状态行,然后把报头信息一一取出拼接在状态行的后面,for结束后标识状态行和报头信息已经填好了,接下来就是空行和文本内容的填充,把填充完整的响应返回。

const std::string BlankSep=" ";
const std::string LineSep="\r\n";class HttpResponse
{
public:HttpResponse():_http_version("HTTP/1.0"),_status_code(200),_status_code_desc("OK"),_resp_blank(LineSep){}void SetCode(int code){_status_code=code;}void SetDesc(const std::string& desc){_status_code_desc=desc;}void MakeStatusLine(){_status_line=_http_version+BlankSep+std::to_string(_status_code)+BlankSep+_status_code_desc+LineSep;}void AddHeader(const std::string& header){_resp_header.push_back(header+LineSep);}void AddContent(const std::string& content){_resp_content=content;}std::string Serialize(){MakeStatusLine();std::string response_str=_status_line;for(auto& header:_resp_header){response_str+=header;}response_str+=_resp_blank;response_str+=_resp_content;return response_str;}~HttpResponse(){}private:std::string _status_line;std::vector<std::string> _resp_header;std::string _resp_blank;std::string _resp_content;std::string _http_version;int _status_code;std::string _status_code_desc;};

这个类就是网络处理部分。构造函数要接收一个端口号,make_unique构造一个TcpServer对象出来,参数是端口号和一个bind对象,再调用Init函数。ProveCookieWrite函数和ProveCookieTimeOut函数都是添加报头信息,一个是用户名一个是用户名和过期时间(这里设置一分钟),ProvePath函数添加了路径,ProveOtherCookie添加了密码。HandlerHttp函数就是处理部分了,创建请求对象,调用请求的反序列化方法,显示报头信息,创建响应对象,设置状态码,状态信息,添加报头内容,添加文本内容helloworld(前端格式写的,最后网页访问会看到helloworld),最后返回完整的且序列化后的信息。

class Http
{std::string GetMonthName(int month){std::vector<std::string> months={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};return months[month];}std::string GetWeekDayName(int day){std::vector<std::string> weekdays={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};return weekdays[day];}std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间{time_t timeout = time(nullptr) + t;struct tm *tm = gmtime(&timeout); // 这里不能用localtime,因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间char timebuffer[1024];//时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTCsnprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC", GetWeekDayName(tm->tm_wday).c_str(),tm->tm_mday,GetMonthName(tm->tm_mon).c_str(),tm->tm_year+1900,tm->tm_hour,tm->tm_min,tm->tm_sec);return timebuffer;}
public:Http(uint16_t port){_tsvr=std::make_unique<TcpServer>(port,std::bind(&Http::HandlerHttp,this,std::placeholders::_1));_tsvr->Init();}std::string ProveCookieWrite(){return "Set-Cookie: username=zhangsan;";}std::string ProveCookieTimeOut(){return "Set-Cookie: username=zhangsan;expires="+ExpireTimeUseRfc1123(60)+";";}std::string ProvePath(){return "Set-Cookie: username=zhangsan; path=/a/b";}std::string ProveOtherCookie(){return "Set-Cookie: passwd=1234567890; path=/a/b;";}std::string HandlerHttp(std::string request){HttpRequest req;req.Deserialize(request);req.DebugHttp();lg.LogMessage(Debug,"%s\n",ExpireTimeUseRfc1123(60).c_str());HttpResponse resp;resp.SetCode(200);resp.SetCode(200);resp.SetDesc("OK");resp.AddHeader("Content-Type: text/html");resp.AddHeader(ProvePath());resp.AddHeader(ProveOtherCookie());resp.AddContent("<html><h1>helloworld</h1></html>");return resp.Serialize();}void Run(){_tsvr->Start();}~Http(){}
private:std::unique_ptr<TcpServer> _tsvr;
};

TcpServer.hpp文件

构造函数接收端口号和回调函数,以及套接字的值初始化等。ProcessConnection函数参数为Socket对象和InetAddr对象,调用sock方法recv在创建的套接字中读取内容,调用handler处理读取到的信息,把响应信息发送回去,关闭套接字(关闭后就不能传入命令了 telnet指令)。Start函数则会调用AcceptConnection函数进行连接(Tcp协议需要双方连接才能通信),创建task_t对象赋值为bind对象,bind是第一个是函数地址,但是类的话不是这样,类函数需要&来获取函数地址,后面的都是参数,而类函数还需要传递this指针,因为类函数隐式参数this。接着再创建实例并调用插入函数把task插入进去。

#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <functional>
#include <memory>
#include "ThreadPool.hpp"
#include "Log.hpp"
#include "Comm.hpp"
#include "Socket.hpp"
#include "InetAddr.hpp"using namespace Net_Work;
const static int default_backlog = 6;
using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string)>;class TcpServer
{
public:TcpServer(uint16_t port,handler_t handler):_port(port),_isrunning(false),_listensock(new TcpSocket()),_handler(handler){}void Init(){_listensock->BuildListenSocketMethod(_port,default_backlog);}void ProcessConnection(std::shared_ptr<Socket> sock,InetAddr addr){std::string request_str;if(sock->Recv(&request_str,4096)){std::string response=_handler(request_str);sock->Send(response);sock->CloseSocket();}}void Start(){_isrunning=true;while(_isrunning){std::string clientip;uint16_t clientport;std::shared_ptr<Socket> sock=_listensock->AcceptConnection(&clientip,&clientport);if(sock==nullptr){continue;}InetAddr addr(clientip,clientport);task_t task=std::bind(&TcpServer::ProcessConnection,this,sock,addr);ThreadPool<task_t>::GetInstance()->Push(task);}_isrunning=false;}~TcpServer(){}
private:uint16_t _port;std::unique_ptr<Socket> _listensock;bool _isrunning;handler_t _handler;
};

成员函数与非成员函数的区别

  • 非成员函数 :对于非成员函数(即全局函数或命名空间内的函数),函数名本身可以直接作为函数的指针。例如,对于一个非成员函数 void foo(),可以将 foo 作为函数指针使用。

  • 成员函数 :成员函数属于类的一部分,它们的调用需要一个对象上下文(即通过对象来调用)。因此,要获取成员函数的地址,必须使用 & 操作符。例如,对于类 A 中的成员函数 void bar(),要获取其地址,必须写成 &A::bar

Main.cc文件

#include "HttpProtocol.hpp"
#include <memory>using namespace std;void Usage(std::string proc)
{std::cout<<"Usage: \n\t"<<proc<<" local_port\n"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);return Usage_Err;}uint16_t port=stoi(argv[1]);std::unique_ptr<Http> http=make_unique<Http>(port);http->Run();return 0;
}

ThreadPool.hpp文件

#pragma once#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"static const int defaultnum=5;class ThreadData
{
public:ThreadData(const std::string& name):threadname(name){}~ThreadData(){}
public:std::string threadname;
};template<class T>
class ThreadPool
{
private:ThreadPool(int thread_num=defaultnum):_thread_num(thread_num){pthread_mutex_init(&_mutex,nullptr);pthread_cond_init(&_cond,nullptr);for(int i=0;i<_thread_num;i++){std::string threadname="thread-";threadname+=std::to_string(i+1);ThreadData td(threadname);_threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun,this,std::placeholders::_1),td);lg.LogMessage(Info,"%s is created...\n", threadname.c_str());}}ThreadPool(const ThreadPool<T>& tp)=delete;const ThreadPool<T>& operator=(const ThreadPool<T>)=delete;public:static ThreadPool<T>* GetInstance(){if(instance==nullptr){LockGuard lockguard(&sig_lock);if(instance==nullptr){lg.LogMessage(Info, "创建单例成功...\n");instance =new ThreadPool<T>();instance->Start();}}return instance;}bool Start(){for(auto& thread:_threads){thread.Start();lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());}return true;}void ThreadWait(const ThreadData& td){lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());pthread_cond_wait(&_cond,&_mutex);}void ThreadWakeup(){pthread_cond_signal(&_cond);}void checkSelf(){}void ThreadRun(ThreadData &td){while(true){T t;{LockGuard lockguard(&_mutex);while(_q.empty()){ThreadWait(td);lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());}t=_q.front();_q.pop();}t();}}void Push(T& in){LockGuard Lockguard(&_mutex);_q.push(in);ThreadWakeup();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}void Wait(){for(auto& thread: _threads){thread.Join();}}
private:std::queue<T> _q;std::vector<Thread<ThreadData>> _threads;int _thread_num;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T>* instance;static pthread_mutex_t sig_lock;};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

thread.hpp文件

#pragma once#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;template<class T>
class Thread
{
public:Thread(const std::string &threadname, func_t<T> func, T &data):_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data){}static void *ThreadRoutine(void *args) // 类内方法,{// (void)args; // 仅仅是为了防止编译器有告警Thread *ts = static_cast<Thread *>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);if(n == 0) {_isrunning = true;return true;}else return false;}bool Join(){if(!_isrunning) return true;int n = pthread_join(_tid, nullptr);if(n == 0){_isrunning = false;return true;}return false;}std::string ThreadName(){return _threadname;}bool IsRunning(){return _isrunning;}~Thread(){}
private:pthread_t _tid;std::string _threadname;bool _isrunning;func_t<T> _func;T _data;
};

代码流程

main中unique_ptr创建了Http对象的智能指针,就会调用Http类的构造函数,会给Http的tsvr赋值上一个make出来的TcpServer对象,参数是端口号和bind对象,Tcp类也会调用构造函数确定了端口号和回调函数handler,接着调用init函数,里面会调用BuildListenSocketMethod创建套接字并初始化。这时走完了main的第20行代码,接着21调用http的run函数,里面调用tsvr的Start函数,又到了Tcpserver部分,这里就会开始连接然后调用GetInstacnce函数,此时会跳到threadPool部分,执行threadpool的构造函数,锁和条件变量初始化,然后开始往_threads里面插入线程名字和bind对象(ThreadRun函数),因为_thread<Thread<ThreadData>>,所以就会调用thread构造函数,确定了回调函数是Threadrun函数,然后再继续执行GetInstance函数,开辟空间调用线程池的Start函数,start函数会调用所有线程的start函数(thread.start()),就会跳到thread部分,这里就会创建线程执行routine函数,然后调用回调函数threadrun,参数是线程对象ThreadData型,就会回到线程池部分,run会一直检查任务队列是否为空,为空就把这个线程放到条件变量中,不为空就取出任务队列并执行,这里是全部到条件变量中等待,还没有插入。此时才到Push(task)地方,开始插入任务,然后唤醒线程,唤醒的线程就会执行routine函数走回调函数func(Threadrun函数),run函数while队列不是空,就可以取出任务执行了,而这个任务就是httphandler函数,处理请求,构造响应返回。

4.session实现

HttpProcotol.hpp文件

请求类方法实现,GetLine把套接字读取的内容进行行划分,以间隔号作为基准划分多行。Parse函数把提取的行进行分离,stringstream字符串流,会以空格号为间隔把方法,uri和版本号提取出来存储在成员变量中,接着定义prefix,然后范围for遍历报头信息,如果报头信息中有与prefix一样的字符串就把跟prefix一样的部分之后的地方提取出来存储在cookie中,在插入到vector中管理,接着改变prefix值,在来一次,这次找的就是sessionid=,会提取=后面的值存储到vector中管理。

#pragma once#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include <functional>
#include "TcpServer.hpp"
#include "Session.hpp" // 引入sessionconst std::string HttpSep = "\r\n";
// 可以配置的
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";class HttpRequest
{
public:HttpRequest():_req_blank(HttpSep),_path(wwwroot){}bool GetLine(std::string& str,std::string* line){auto pos=str.find(HttpSep);if(pos==std::string::npos){return false;}*line=str.substr(0,pos);str.erase(0,pos+HttpSep.size());return true;}void Parse(){std::stringstream ss(_req_line);ss>>_method>>_url>>_http_version;std::string prefix="Cookie:";for(auto& line:_req_header){std::string cookie;if(strncmp(line.c_str(),prefix.c_str(),prefix.size())==0){cookie=line.substr(prefix.size());_cookies.emplace_back(cookie);break;}}prefix="sessionid=";for(const auto& cookie:_cookies){if(strncmp(cookie.c_str(),prefix.c_str(),prefix.size())==0){_sessionid=cookie.substr(prefix.size());}}}std::string Url(){return _url;}std::string SessionId(){return _sessionid;}bool Deserialize(std::string& request){std::string line;bool ok=GetLine(request,&line);if(!ok)return false;_req_line=line;while(true){bool ok=GetLine(request,&line);if(ok&& line.empty()){_req_content=request;break;}else if(ok&& !line.empty()){_req_header.push_back(line);}else{break;}}return true; }void DebugHttp(){std::cout<<"_req_line:"<<_req_line<<std::endl;for(auto& line:_req_header){std::cout<<"--->"<<line<<std::endl;}}~HttpRequest(){}
private:std::string _req_line;std::vector<std::string> _req_header;std::string _req_blank;std::string _req_content;std::string _method;std::string _url;std::string _http_version;std::string _path;std::string _suffix;std::vector<std::string> _cookies;std::string _sessionid;
};

 处理请求的地方,构建请求和应答类,反序列化请求并提取请求行的信息,定义number变量,走if判断,如果为login就说明要登陆,然后提取sessionid值,如果为空说明没有登陆过,定义用户名为number+1,创建session对象,会走session构造函数,得到名字和状态,调用Sessionmanager类方法Addsession插入创建的s对象,会创建一个sessionid值出来,通过随机数加时间戳,并把创建好的sessionid和s存储到map里面管理,把sessionid信息添加到报头中。如果不是login则会查看sessionid值,不为空就调用Getsession函数,在Session.hpp文件中的SessionManger类中成员变量_sessions中查看是否存在这个sessionid,存在就打印正在活跃,没有就标识过期,后面就是设置状态码状态信息等内容。

 std::string HandlerHttp(std::string request){HttpRequest req;HttpResponse resp;req.Deserialize(request);req.Parse();static int number=0;if(req.Url()=="/login"){std::string sessionid=req.SessionId();if(sessionid.empty()){std::string user="user-"+std::to_string(number++);session_ptr s=std::make_shared<Session>(user,"logined");std::string sessionid=_session_manager->AddSession(s);lg.LogMessage(Debug, "%s 被添加, sessionid是: %s\n", user.c_str(), sessionid.c_str());resp.AddHeader(ProveSession(sessionid));}}else{std::string sessionid=req.SessionId();if(!sessionid.empty()){session_ptr s=_session_manager->GetSession(sessionid);if(s!=nullptr)lg.LogMessage(Debug, "%s 正在活跃.\n", s->_username.c_str());elselg.LogMessage(Debug, "cookie : %s 已经过期, 需要清理\n", sessionid.c_str()); }}resp.SetCode(200);resp.SetDesc("OK");resp.AddHeader("Content-Type:text/html");resp.AddContent("<html><h1>helloworld</h1></html>");return resp.Serialize();}

Session.hpp文件

Session类主要是定义用户名字和访问什么文件,SessionManger则是管理session的类,添加session和检验并返回session。

#pragma once#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include <unistd.h>
#include <unordered_map>// 用来进行测试说明
class Session
{
public:Session(const std::string &username, const std::string &status):_username(username), _status(status){_create_time = time(nullptr); // 获取时间戳就行了,后面实际需要,就转化就转换一下}~Session(){}
public:std::string _username;std::string _status;uint64_t _create_time;uint64_t _time_out; // 60*5std::string vip; // vipint active; // std::string pos;//当然还可以再加任何其他信息,看你的需求
};using session_ptr = std::shared_ptr<Session>;class SessionManager
{
public:SessionManager(){srand(time(nullptr) ^ getpid());}std::string AddSession(session_ptr s){uint32_t randomid = rand() + time(nullptr); // 随机数+时间戳,实际有形成sessionid的库,比如boost uuid库,或者其他第三方库等std::string sessionid = std::to_string(randomid);_sessions.insert(std::make_pair(sessionid, s));return sessionid;}session_ptr GetSession(const std::string sessionid){if(_sessions.find(sessionid) == _sessions.end()) return nullptr;return _sessions[sessionid];}~SessionManager(){}
private:std::unordered_map<std::string, session_ptr> _sessions;
};

http://www.xdnf.cn/news/3848.html

相关文章:

  • 洛谷 P1850 [NOIP 2016 提高组] 换教室
  • 【家政平台开发(100)】终结篇,破局·拓新:家政平台未来发展的战略蓝图
  • 安卓基础(startActivityForResult和onActivityResult)
  • 【Mytais系列】Update语句执行流程
  • 二、shell脚本--变量与数据类型
  • Python datetime库的用法 Python从入门到入土系列第3篇-洞察标准库DateTime
  • 【Spring】Spring中8种常见依赖注入使用示例
  • 健康养生新主张
  • web应用开发说明文档
  • matlab学习之旅
  • 数据结构---
  • 实战项目:基于控制台与数据库的图书管理系统开发指南
  • C语言中memmove和memcpy
  • 智慧校园整体解决方案-5PPT(65页)
  • python中的异常处理
  • 【CF】Day50——Codeforces Round 960 (Div. 2) BCD
  • 数学实验Matlab
  • 多把锁以及线程死锁问题
  • Linux-GRUB全面指南
  • CUDA输出“hello world”
  • 多数据源动态切换
  • 算法每日一题 | 入门-顺序结构-数字反转
  • (38)VTK C++开发示例 ---纹理裁剪
  • C++负载均衡远程调用学习之异步消息任务功能与连接属性
  • CVPR2021 | 重新思考视觉Transformer中的自注意力机制
  • Java学习手册:Spring 生态其他组件介绍
  • 单细胞测序试验设计赏析(一)
  • AWS在跨境电商中的全场景实践与未来生态构建
  • D. 例题3.2.2 整数划分问题
  • 二种MVCC对比分析