1.Redis相关介绍;
C++连接redis工具:
hiredis安装
cd /home/shiyanlou
wget https://github.com/redis/hiredis/archive/v0.14.0.tar.gz
tar -xzf v0.14.0.tar.gz
cd hiredis-0.14.0/
make
sudo make install
##复制路径
sudo cp /usr/local/lib/libhiredis.so.0.14 /usr/lib/
测试程序:
#include <iostream>
#include <hiredis/hiredis.h>
using namespace std;
int main(){
redisContext *c = redisConnect("127.0.0.1",6379);
if(c->err){
redisFree(c);
cout<<"连接失败"<<endl;
return 1;
}
cout<<"连接成功"<<endl;
redisReply *r = (redisReply*)redisCommand(c,"PING");
cout<<r->str<<endl;
return 0;
}
打开redis:
cd /usr/bin
./redis-server
打开新终端:
g++ -o test_redis test_redis.cpp -lhiredis
./test_redis##运行程序
2 服务端
2.1 功能概述:
利用 Redis 记录用户登录状态(HASH 类型,键为 sessionid,值为 session 对象,键五分钟后过期),当用户成功登录时服务器会利用随机算法生成 sessionid 发送到客户端保存,客户登录时会优先发送 sessionid到服务器检查,如果检查通过就不用输入账号密码登录。
2.2头文件添加:
global.h
#ifndef _GLOBAL_H
#define _GLOBAL_H
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <iostream>
#include <thread>
#include <vector>
#include <mysql/mysql.h>
#include <unordered_map>
#include <pthread.h>
#include <set>
#include <hiredis/hiredis.h> //补充
using namespace std;
#endif
2.3HandleRequest代码修改:
- redis的连接:
//连接redis数据库
redisContext *redis_target = redisConnect("127.0.0.1",6379);
if(redis_target->err){
redisFree(redis_target);
cout<<"连接redis失败"<<endl;
}
- 查看是否存在session
//先接收cookie看看redis是否保存该用户的登录状态
if(str.find("cookie:")!=str.npos){
string cookie=str.substr(7);
// 查询该cookie是否存在:hget cookie name
string redis_str="hget "+cookie+" name";
redisReply *r = (redisReply*)redisCommand(redis_target,redis_str.c_str());
string send_res;
//存在
if(r->str){
cout<<"查询redis结果:"<<r->str<<endl;
send_res=r->str;
}
//不存在
else
send_res="NULL";
send(conn,send_res.c_str(),send_res.length()+1,0);
}
- 生成sessionId并发送
生成SessionId算法:10位sessionid,每位由大写字母小写字母以及数字中随机生成;
//登录
else if(str.find("login")!=str.npos){
int p1=str.find("login"),p2=str.find("pass:");
name=str.substr(p1+5,p2-5);
pass=str.substr(p2+5,str.length()-p2-4);
string search="SELECT * FROM USER WHERE NAME=\"";
search+=name;
search+="\";";
cout<<"sql语句:"<<search<<endl;
auto search_res=mysql_query(con,search.c_str());
auto result=mysql_store_result(con);
int col=mysql_num_fields(result);//获取列数
int row=mysql_num_rows(result);//获取行数
//查询到用户名
if(search_res==0&&row!=0){
cout<<"查询成功\n";
auto info=mysql_fetch_row(result);//获取一行的信息
cout<<"查询到用户名:"<<info[0]<<" 密码:"<<info[1]<<endl;
//密码正确
if(info[1]==pass){
cout<<"登录密码正确\n\n";
string str1="ok";
if_login=true;
login_name=name;
pthread_mutex_lock(&name_sock_mutx); //上锁
name_sock_map[login_name]=conn;//记录下名字和文件描述符的对应关系
pthread_mutex_unlock(&name_sock_mutx); //解锁
// 随机生成sessionid并发送到客户端
srand(time(NULL));//初始化随机数种子
for(int i=0;i<10;i++){
int type=rand()%3;//type为0代表数字,为1代表小写字母,为2代表大写字母
if(type==0)
str1+='0'+rand()%9;
else if(type==1)
str1+='a'+rand()%26;
else if(type==2)
str1+='A'+rand()%26;
}
//将sessionid存入redis
string redis_str="hset "+str1.substr(2)+" name "+login_name;
redisReply *r = (redisReply*)redisCommand(redis_target,redis_str.c_str());
//设置生存时间,默认300秒
redis_str="expire "+str1.substr(2)+" 300";
r=(redisReply*)redisCommand(redis_target,redis_str.c_str());
cout<<"随机生成的sessionid为:"<<str1.substr(2)<<endl;
send(conn,str1.c_str(),str1.length()+1,0);
}
//密码错误
else{
cout<<"登录密码错误\n\n";
char str1[100]="wrong";
send(conn,str1,strlen(str1),0);
}
}
//没找到用户名
else{
cout<<"查询失败\n\n";
char str1[100]="wrong";
send(conn,str1,strlen(str1),0);
}
}
3.客户端
3.1 功能概述
大致步骤:在本地创建cookie.txt 文件存储sessionId;
第一次登录,接受sessionid存储至cookie.txt当中;
将其发送至服务端,和redis 中数据进行对比;
由服务端发送结果到客户端;
若对比成功,则直接登录成功;
3.2对比sessionId;
ifstream f("cookie.txt");
string cookie_str;
if(f.good()){
f>>cookie_str;
f.close();
cookie_str="cookie:"+cookie_str;
//将cookie发送到服务器
send(sock,cookie_str.c_str(),cookie_str.length()+1,0);
//接收服务器答复
char cookie_ans[100];
memset(cookie_ans,0,sizeof(cookie_ans));
recv(sock,cookie_ans,sizeof(cookie_ans),0);
//判断服务器答复是否通过
string ans_str(cookie_ans);
if(ans_str!="NULL"){//redis查询到了cookie,通过
if_login=true;
login_name=ans_str;
}
3.3登录新建cookie.txt
//开始处理注册、登录事件
while(1){
if(if_login)
break;
cin>>choice;
if(choice==0)
break;
//注册
else if(choice==2){
cout<<"注册的用户名:";
cin>>name;
while(1){
cout<<"密码:";
cin>>pass;
cout<<"确认密码:";
cin>>pass1;
if(pass==pass1)
break;
else
cout<<"两次密码不一致!\n\n";
}
name="name:"+name;
pass="pass:"+pass;
string str=name+pass;
send(conn,str.c_str(),str.length(),0);
cout<<"注册成功!\n";
cout<<"\n继续输入你要的选项:";
}
//登录
else if(choice==1&&!if_login){
while(1){
cout<<"用户名:";
cin>>name;
cout<<"密码:";
cin>>pass;
string str="login"+name;
str+="pass:";
str+=pass;
send(sock,str.c_str(),str.length(),0);//发送登录信息
char buffer[1000];
memset(buffer,0,sizeof(buffer));
recv(sock,buffer,sizeof(buffer),0);//接收响应
string recv_str(buffer);
if(recv_str.substr(0,2)=="ok"){
if_login=true;
login_name=name;
string tmpstr=recv_str.substr(2);
tmpstr="cat > cookie.txt <<end \n"+tmpstr+"\nend";
system(tmpstr.c_str());
cout<<"登陆成功\n\n";
break;
}
else
cout<<"密码或用户名错误!\n\n";
}
}
}
//登陆成功
while(if_login&&1)```
## 4 功能展示;
服务端
![在这里插入图片描述](https://img-blog.csdnimg.cn/0952f357597843d991329a9fdba01bfa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiJ4oWm,size_20,color_FFFFFF,t_70,g_se,x_16)
客户端:
![在这里插入图片描述](https://img-blog.csdnimg.cn/fb2619ce366a4ed7ab9372b3e99a9fe8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiJ4oWm,size_19,color_FFFFFF,t_70,g_se,x_16)