文章目录
- 1.项目介绍
- 2.前端代码
- 1.httplib快速搭建一个http服务器
- 2.B/S双方的数据交互选择JSON数据格式,http请求和响应的正文中采用jsoncpp开源库
- 3.前段的js代码向发送ajax请求
- 三、服务端代码
- 1.搭建一个music_player这样的一个类
- 2.增加注册按钮,并且点击注册按钮跳转到注册界面
- 3.服务端代码当中调用mysql-c api连接数据库,进行操作
- 4.登录查询用户是否存在校验邮箱和密码
- 5.通过命令行参数获取ip地址和端口号信息
- 6.保存登录的会话信息session,使用md5摘要保护登录信息,否则造成直接跳转list.html界面
- 7.用哈希表保存多个用户的信息
- 8.数据库存放音乐相关信息,通过json串返回给浏览器
- 9.查询音乐、喜欢的音乐、添加喜欢的音乐
1.项目介绍
音乐播放器拥有注册登录功能,查看所有的音乐列表,也可以听音乐,也可以将音乐添加到自己的喜欢列表。
2.前端代码
1.httplib快速搭建一个http服务器
1 #include<stdio.h>
2 #include<iostream>
3
4 #include<string.h>
5 #include"httplib.h"
6
7 using namespace httplib;
8 using namespace std;
9
10 int g_val=100;
11
12 void Get_CallBackFunc(const Request& req, Response& resp)
13 {
14 printf("%d\n",g_val);
15 cout<<req.method<<endl;
16 printf("i am Get_CallBackFunc\n");
17
18 const char* lp="<html><h2>hello world!</h2></html>";
19 resp.set_content(lp,strlen(lp),"text/html");
20 }
21
22
23
24 int main()
25 {
26 Server http_svr;
27 int a=10;
28
29 http_svr.Get("/abc",Get_CallBackFunc);
30 http_svr.Post("/111",[a](const Request& req,Response& resp){
31 printf("a:%d\n",a);
32
33 });
34 //直接访问web里面的文件
35 //逻辑根目录指定为web文件夹
36 http_svr.set_mount_point("/","./web");
37 http_svr.listen("0.0.0.0",21010);
38
39 return 0;
40 }
2.B/S双方的数据交互选择JSON数据格式,http请求和响应的正文中采用jsoncpp开源库
1 #include<stdio.h>
2 #include<iostream>
3 #include<jsoncpp/json/json.h>
4
5 using namespace std;
6
7 int main()
8 {
9
10 Json::Value v1;
11 v1["id"]=1;
12 v1["name"]="yyw";
13 cout<<v1<<endl;
14
15 Json::Value v2;
16 v2["id"]=2;
17 v2["name"]="yy";
18 cout<<v2<<endl;
19
20 Json::Value v3;
21 v3["v1"]=v1;
22 v3["v2"]=v2;
23 cout<<v3<<endl;
24
25 Json::Value v4;
26 v4.append(v1);
27 v4.append(v2);
28 cout<<v4<<endl;
29
30 for(int i=0;i<2;i++)
31 {
32 cout<<v4[i]["id"]<<":"<<v4[i]["name"]<<endl;
33 }
34
35 //序列化
36 Json::FastWriter w;
37 string json_str=w.write(v4);
38 cout<<json_str<<endl;
39 //反序列化
40 Json::Reader r;
41 Json::Value vv;
42 r.parse(json_str,vv);
43 cout<<vv<<endl;
44 return 0;
45 }
JSON数据格式有三种显示方法:平铺、嵌套、数组形式。
序列化和反序列化:
3.前段的js代码向发送ajax请求
#include<stdio.h>
#include<iostream>
#include<jsoncpp/json/json.h>
#include<string.h>
#include"httplib.h"
using namespace httplib;
using namespace std;
int g_val=100;
void Get_CallBackFunc(const Request& req, Response& resp)
{
printf("%d\n",g_val);
cout<<req.method<<endl;
printf("i am Get_CallBackFunc\n");
const char* lp="<html><h2>hello world!</h2></html>";
resp.set_content(lp,strlen(lp),"text/html");
}
int main()
{
Server http_svr;
http_svr.Get("/abc",Get_CallBackFunc);
//发送post请求/login,回调这个表达式
http_svr.Post("/login",[](const Request& req,Response& resp){
cout<<req.body<<endl; //打印正文信息
Json::Value resp_json;
resp_json["login_status"]=true; //浏览器返回登录状态
//序列化
//系列化成完整的字符串的内容放到响应的正文中
Json::FastWriter w;
resp.body= w.write(resp_json);
//告诉浏览器返回的内容就是json串,对json解析
resp.set_header("Content-Type","application/json");
});
//直接访问web里面的文件
//逻辑根目录指定为web文件夹
http_svr.set_mount_point("/","./web");
http_svr.listen("0.0.0.0",21010);
return 0;
}
三、服务端代码
1.搭建一个music_player这样的一个类
1 #pragma once
2 #include"database.hpp"
3 #include<stdio.h>
4 #include<iostream>
5 #include<string>
6 #include"httplib.h"
7
8 #include<jsoncpp/json/json.h>
9
10 using namespace std;
11
12 using namespace httplib;
13
14 #define MUSIC_SVR_IP "0.0.0.0"
15 #define MUSIC_SVR_PORT 18989
16
17 class MusicServer
18 {
19 public:
20 MusicServer()
21 {
22 svr_ip_=MUSIC_SVR_IP;
23 svr_port_=MUSIC_SVR_PORT;
24 db_svr_=NULL;
25 }
26 ~MusicServer()
27 {
28
29 }
30
31 //1.初始化当前类接
32 int InitMusicServer(string ip=MUSIC_SVR_IP,uint16_t port=MUSIC_SVR_PORT)
33 {
34 svr_ip_=ip;
35 svr_port_=port;
36
37 db_svr_=new DataBaseSvr("1.14.165.138","yy","123","music_svr");
38 if(db_svr_==NULL)
39 {
40 return -1;
41 }
42 return 0;
43 }
44 //2.启动服务的接口
45 int StartMusicServer()
46 {
47 //1.注册个若干个http请求的对应的回调函数
48 /*
49 *请求:{"name":"yy","passwd":"123","email":"123@qq.com","phonenum":"1231"}
50 *响应:{"register_status":"xxxx"}
51 *
52 *
53 * */
54 http_svr_.Post("/register",[this](const Request& req,Response& resq){
55 cout<<req.body<<endl;
56 //1.需要将浏览器中的数据持久化(保存在数据库中)
57 //
58 //1.将用户提交的数据进行反序列化,拿到一个json对象
59 Json::Reader r;
60 Json::Value v;
61 r.parse(req.body,v);
62
63 cout<<v["name"]<<endl;
64 cout<<v["passwd"]<<endl;
65 cout<<v["email"]<<endl;
66 cout<<v["phonenum"]<<endl;
67
68 db_svr_->InsertUserInfo(v);
69 });
70
71 http_svr_.Post("/login",[](const Request& req,Response& resq){
72 /*
73 *1.
74 *2.
75 *3.
76 * */
77 cout<<req.body<<endl;
78
79 });
80
81 //2.设置http服务器静态路径(逻辑根目录)
82 http_svr_.set_mount_point("/","./web");
83 //3.监听起来
84 http_svr_.listen(svr_ip_.c_str(),svr_port_);
85 }
86 private:
87 //httplib 当中的server对象
88 Server http_svr_;
89
90 string svr_ip_;
91 uint16_t svr_port_;
92
93 //数据库操作模块
94 DataBaseSvr* db_svr_;
95 };
2.增加注册按钮,并且点击注册按钮跳转到注册界面
54 http_svr_.Post("/register",[this](const Request& req,Response& resq){
55 cout<<req.body<<endl;
56 //1.需要将浏览器中的数据持久化(保存在数据库中)
57 //
58 //1.将用户提交的数据进行反序列化,拿到一个json对象
59 Json::Reader r;
60 Json::Value v;
61 r.parse(req.body,v);
62
63 cout<<v["name"]<<endl;
64 cout<<v["passwd"]<<endl;
65 cout<<v["email"]<<endl;
66 cout<<v["phonenum"]<<endl;
67
68 db_svr_->InsertUserInfo(v);
69 });
3.服务端代码当中调用mysql-c api连接数据库,进行操作
1 #pragma once
2 #include<stdio.h>
3 #include<unistd.h>
4 #include<iostream>
5 #include<string>
6
7 #include<mysql/mysql.h>
8 #include<jsoncpp/json/json.h>
9 using namespace std;
10 class DataBaseSvr
11 {
12 public:
13 DataBaseSvr(const string& db_host,const string& db_user,const string& db_password,const string& db_name,unsigned int db_port=3306)
14 {
15 mysql_init(&mysql_);
16 db_host_=db_host;
17 db_user_=db_user;
18 db_password_=db_password;
19 db_port_=db_port;
20 db_name_=db_name;
21 }
22 ~DataBaseSvr()
23 {
24
25 }
26
27 int ConnectToMysql()
28 {
29 if(!mysql_real_connect(&mysql_,db_host_.c_str(),db_user_.c_str(),db_password_.c_str(),db_name_.c_str(),db_port_,NULL,CLIENT_FOUND_ROWS))
30 {
31 cout<<mysql_error(&mysql_)<<endl;
32 return -1;
33 }
34 return 0;
35 }
36
37 //提交上来的对象
38 int InsertUserInfo(const Json::Value& v)
39 {
40 //1.连接数据库
41 if(ConnectToMysql()<0)
42 {
43 return -1;
44 }
45 //2.组织sql语
46
47 string name=v["name"].asString();
48 string password=v["password"].asString();
49 string email=v["email"].asString();
50 string phonenum=v["phonenum"].asString();
51
52 #define INSERT_USER_INFO "insert into sys_user(user_name,password,email,phone_num) values('%s','%s','%s','%s');"
53
54 char sql[1024]={0};
55 snprintf(sql,sizeof(sql)-1,INSERT_USER_INFO,name.c_str(),password.c_str(),email.c_str(),phonenum.c_str());
56 cout<<"sql:"<<sql<<endl;
57
58 //3.继续执行sql
59 //4.返回插入结果给调用者
60 }
61 private:
62 MYSQL mysql_;
63
64 string db_host_;
65 string db_user_;
66 string db_password_;
67 string db_name_;
68 unsigned int db_port_;
69 };
4.登录查询用户是否存在校验邮箱和密码
//查询用户是否存在
105 int QueryUserExist(const Json::Value& v)
106 {
107 /* //0 链接
108 if(ConnectToMysql()<0)
109 {
110 return -1;
111 }*/
112 //1.从json对象当中解析出来 邮箱和密码
113
114 string email = v["email"].asString();
115 string password = v["password"].asString();
116
117 //2.使用邮箱作为查询条件在sys_user表当中进行查询(如果没有查找到用户,则返回)
118 #define QUERY_USER "select * from sys_user where email='%s';"
119 char sql[1024] = {0};
120 snprintf(sql, sizeof(sql) - 1, QUERY_USER, email.c_str());
121 cout << sql << endl;
122
123 //3.针对查询的结果当中的密码, 进行比对
124 MYSQL_RES* res = NULL;
125 if(ExecuteSql(sql, &res) == false)
126 {
127 return -2;
128 }
129
130 //3.1 针对结果集进行操作, 判断下,结果集当中的行数是否等于1
131 // 等于1, 继续往下执行
132 // 不等1 , 直接返回
133 //my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res);
134 if(mysql_num_rows(res) != 1)
135 {
136 cout << "No Data for sql!, sql is " << sql <<endl;
137 mysql_free_result(res);
138 return -3;
139 }
140
141 //3.2 在结果集当中获取一行数据
142 //MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
143 // 如果重复调用msyql_fetch_row这个函数, 默认是取下一行的数据
144 MYSQL_ROW row = mysql_fetch_row(res);
145
146 //3.3 比对密码
147 string db_password = row[2];
148 if(db_password != password )
149 {
150 return -4;
151 }
152
153 mysql_free_result(res);
154 //4.返回查询结果
155 return atoi(row[0]);
156 }
5.通过命令行参数获取ip地址和端口号信息
1 #include"music_player.hpp"
2 void Usage()
3 {
4 cout << "./MusicServer [command key] [command value] ..." << endl;
5 cout << " " << "-ip : svr listen ip" << endl;
6 cout << " " << "-port : svr listen port" << endl;
7 cout << " " << "-db_ip : msyql server ip address" << endl;
8 cout << " " << "-db_port : mysql port" << endl;
9 cout << " " << "-db_user : mysql user" << endl;
10 cout << " " << "-db_passwd : mysql password" << endl;
11 cout << " " << "-db_db : which database " << endl;
12 cout << " " << "-h : show usage" << endl;
13 }
14
15 //通过命令行参数获取ip地址和端口号之类的信息
16 int main(int argc, char* argv[])
17 {
18 if(argc == 2 && strcmp(argv[1], "-h") == 0)
19 {
20 Usage();
21 exit(1);
22 }
23 string svr_ip, db_ip, db_user, db_db, db_passwd;
24 uint16_t svr_port, db_port;
25 for(int i = 0; i < argc; i++)
26 {
27 if(strcmp(argv[i], "-ip") == 0 && i + 1 < argc)
28 {
29 svr_ip = argv[i + 1];
30 }
31 else if(strcmp(argv[i], "-port") == 0 && i + 1 < argc)
32 {
33 svr_port = atoi(argv[i + 1]);
34 }
35 else if(strcmp(argv[i], "-db_ip") == 0 && i + 1 < argc)
36 {
37 db_ip = argv[i + 1];
38 }
39 else if(strcmp(argv[i], "-db_port") == 0 && i + 1 < argc)
40 {
41 db_port = atoi(argv[i + 1]);
42 }
43 else if(strcmp(argv[i], "-db_user") == 0 && i + 1 < argc)
44 {
45 db_user = argv[i + 1];
46 }
47 else if(strcmp(argv[i], "-db_db") == 0 && i + 1 < argc)
48 {
49 db_db = argv[i + 1];
50 }
51 else if(strcmp(argv[i], "-db_passwd") == 0 && i + 1 < argc)
52 {
53 db_passwd = argv[i + 1];
54 }
55 }
56 cout<<"please check info:"<<endl;
57 cout << "svr_ip: " << svr_ip << endl;
58 cout << "svr_pot: " << svr_port << endl;
59 cout << "db_ip: " << db_ip << endl;
60 cout << "db_port: " << db_port << endl;
61 cout << "db_user: " << db_user << endl;
62 //cout << "db_passwd: " << db_passwd << endl;
63 cout << "db_db: " << db_db << endl;
64
65 //重新输出ip
66 int select = 0;
67 cout << "enter check result: currect input 1, error input 0;" << endl;
68 cout << "[input select]: ";
69 cin >> select;
70 switch(select)
71 {
72 case 0:
73 cout << "plase retry start server.." << endl;
74 break;
75 case 1:
76 {
77 MusicServer* ms = new MusicServer();
78 if(ms == NULL)
79 {
80 return -1;
81 }
82
83 if(ms->InitMusicServer(db_ip, db_port, db_user, db_passwd, db_db, svr_ip, svr_port) < 0)
84 {
85 return -2;
86 }
87 ms->StartMusicServer();
88 delete ms;
89 break;
90 }
91 default:
92 printf("input select num error, please input 1(currect) or 0(error)\n");
93 break;
94 }
95
96 return 0;
97 }
6.保存登录的会话信息session,使用md5摘要保护登录信息,否则造成直接跳转list.html界面
1 #pragma once
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <openssl/md5.h>
6 #include <iostream>
7 #include <string>
8 #include <unordered_map>
9
10 #include <jsoncpp/json/json.h>
11 #include "httplib.h"
12
13 using namespace std;
14 using namespace httplib;
15
16 typedef enum UserStatus
17 {
18 //不在线
19 OFFLINE = 0,
20 //在线状态
21 ONLINE,
22 }USER_STATUS;
23
24
25 //针对与登录的用户创建的会话信息
26
27 class Session
28 {
29 public:
30 Session()
31 {
32
33 }
34
35 Session(const Json::Value& v, int user_id)
36 {
37 user_id_ = user_id;
38
39 real_str_ += v["email"].asString();
40 real_str_ += v["password"].asString();
41
42 us_status_ = ONLINE;
43 }
44
45 ~Session()
46 {
47
48 }
49
50
51 bool CalcMd5()
52 {
53 MD5_CTX ctx;
54 MD5_Init(&ctx);
55
56 //int MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
57 if(MD5_Update(&ctx, real_str_.c_str(), real_str_.size()) != 1)
58 {
59 return false;
60 }
61
62 //int MD5_Final(unsigned char *md, MD5_CTX *c);
63 //md ==> 16字节
64 unsigned char md5[16] = { 0 };
65 if(MD5_Final(md5, &ctx) != 1)
66 {
67 return false;
68 }
69
70 char tmp[2] = {0};
71 char buf[32] = {0};
72 for(int i = 0; i < 16; i++)
73 {
74 sprintf(tmp, "%02x", md5[i]);
75 strncat(buf, tmp, 2);
76 }
77
78 session_id_ = buf;
79 cout << "session_id_" << session_id_ << endl;
80 return true;
81 }
82
83 string& GetSessionID()
84 {
85 //1.计算sessionID
86 CalcMd5();
87 //2.返回session_id
88 return session_id_;
89 }
90
91 int GetUserId()
92 {
93 return user_id_;
94 }
95
96 private:
97 string session_id_;
98
99 //生成会话ID的源字符串
100 string real_str_;
101
102 int user_id_;
103
104 UserStatus us_status_;
105 };
7.用哈希表保存多个用户的信息
107 //哈希查找多用户的时刻更快
108 class AllSessionInfo
109 {
110 public:
111 AllSessionInfo()
112 {
113 all_sess_map_.clear();
114 pthread_mutex_init(&map_lock_, NULL);
115 }
116
117 ~AllSessionInfo()
118 {
119 pthread_mutex_destroy(&map_lock_);
120 }
121
122 void InsertSessionInfo(const string session_id, const Session sess)
123 {
124 pthread_mutex_lock(&map_lock_);
125 all_sess_map_.insert(make_pair(session_id, sess));
126 pthread_mutex_unlock(&map_lock_);
127 }
128
129 int CheckSession(const Request& req)
130 {
131 /*
132 * 1.通过http请求头当中的 Cookie字段, 获取对应的value(会话ID)
133 * 2.通过会话ID, 在all_sess_map_当中查找, 是否有对应的会话信息
134 * */
135 string sess_id = req.get_header_value("Cookie");
136
137 pthread_mutex_lock(&map_lock_);
138 auto iter = all_sess_map_.find(sess_id);
139 if(iter == all_sess_map_.end())
140 {
141 pthread_mutex_unlock(&map_lock_);
142 return -1;
143 }
144 int user_id = iter->second.GetUserId();
145 pthread_mutex_unlock(&map_lock_);
146 return user_id;
147 }
148 private:
149 unordered_map<string, Session> all_sess_map_;
150 pthread_mutex_t map_lock_;
151 };
8.数据库存放音乐相关信息,通过json串返回给浏览器
int GetMusic(string sql, Json::Value& resp_json)
161 {
162 MYSQL_RES* res = NULL;
163 if(ExecuteSql(sql, &res) == false)
164 {
165 return -2;
166 }
167
168 int row_nums = mysql_num_rows(res);
169 if(row_nums <= 0)
170 {
171 printf("No data: sql is \"%s\"", sql.c_str());
172 mysql_free_result(res);
173 return -3;
174 }
175
176 MYSQL_ROW row = mysql_fetch_row(res);
177 Json::Value music_value;
178 while(row != NULL)
179 {
180 //1.保存数据到json当中
181 Json::Value tmp;
182 tmp["id"] = row[0];
183 tmp["title"] = row[1];
184 tmp["singer"] = row[2];
185 tmp["url"] = row[3];
186
187 music_value.append(tmp);
188 row = mysql_fetch_row(res);
189 }
190
191 resp_json["music"] = music_value;
192 mysql_free_result(res);
193 return 0;
194 }
195
196
197 int GetAllMusic(Json::Value& resp_json)
198 {
199 /*
200 * 1.组织查询的sql语句
201 * 2.调用ExecuteSql函数执行sql语句
202 * 3.检查结果集当中的行数
203 * 4.将获取到的结果集, 进行遍历操作
204 * 4.组织json对象
205 * */
206 const char* sql = "select * from music;";
207 return GetMusic(sql, resp_json);
208 }
209
210
211
212 int InsertLoveMusic(const Request& req, int user_id)
213 {
214 /*
215 * 1.通过req当中的正文信息, 获取music_id
216 * 2.使用music_id, user_id组织sql语句
217 * 3.执行sql语句
218 * */
219 Json::Reader r;
220 Json::Value v;
221 r.parse(req.body, v);
222
223 int music_id = v["music_id"].asInt();
224
225 #define INSERT_LOVE_MUSIC "insert into love_music(user_id, music_id) values(%d, %d);"
226 char sql[1024] = {0};
227 snprintf(sql, sizeof(sql) - 1, INSERT_LOVE_MUSIC, user_id, music_id);
228
229 if(ExecuteSql(sql) == false)
230 {
231 return -1;
232 }
233 return 1;
234 }
235
236 int GetLoveMusic(int user_id, Json::Value& resp_json)
237 {
238 /*
239 * 1.组织查询sql
240 * 2.调用GetMusic函数
241 * */
242 #define GET_LOVE_MUSIC "select * from music where music_id in(select music_id from love_music where user_id=%d);"
243 char sql[1024] = {0};
244 snprintf(sql, sizeof(sql) - 1, GET_LOVE_MUSIC, user_id);
245 return GetMusic(sql, resp_json);
246 }
9.查询音乐、喜欢的音乐、添加喜欢的音乐
http_svr_.Get("/findMusic", [this](const Request& req, Response& resp){
137 /*
138 * 1.会话校验, 从当前http请求的请求体当中拿到 Cookie对应的value值, 会话ID
139 * 1.1 通过会话ID, 在all_sess_当中查找是否有对应的会话
140 * 找到了, 则认为该用户是登录用户, 就可以做后续查找音乐的操作
141 * 没有找到, 则认为该用户是非登录用户, 则返回status为-1
142 * 2.通过数据库模块在数据表music当中查找音乐信息, 则将查找到的音乐信息组织成为json串
143 * 3.组织应答
144 * */
145 Json::Value resp_json;
146 int user_id = all_sess_->CheckSession(req);
147 resp_json["status"] = user_id;
148 if(user_id > 0)
149 {
150 /* 1.会话校验成功的逻辑
151 * 2.访问数据库, 获取到数据表music的音乐信息
152 */
153 db_svr_->GetAllMusic(resp_json);
154 }
155
156 Json::FastWriter w;
157 resp.body = w.write(resp_json);
158 resp.set_header("Content-Type", "application/json");
159 });
160
161 http_svr_.Post("/loveMusic", [this](const Request& req, Response& resp){
162 Json::Value resp_json;
163 int user_id = all_sess_->CheckSession(req);
164 resp_json["status"] = user_id;
165 if(user_id > 0)
166 {
167 cout << "user_id: " << user_id << ", req正文信息:" << req.body << endl;
168 resp_json["status"] = db_svr_->InsertLoveMusic(req, user_id);
169 }
170
171 Json::FastWriter w;
172 resp.body = w.write(resp_json);
173 resp.set_header("Content-Type", "application/json");
174 });
175
176 http_svr_.Get("/findLoveMusic", [this](const Request& req, Response& resp){
177 /*
178 * 1.会话校验,通过会话校验, 如果成功就能获取当前请求的用户ID
179 * 2.查数据库, 当前用户喜欢的音乐
180 * 3.组织json, 将用户喜欢的音乐返回给浏览器
181 * */
182 Json::Value resp_json;
183 int user_id = all_sess_->CheckSession(req);
184 resp_json["status"] = user_id;
185 if(user_id > 0)
186 {
187 //去数据库当中查询喜欢的音乐
188 db_svr_->GetLoveMusic(user_id, resp_json);
189 }
190
191 Json::FastWriter w;
192 resp.body = w.write(resp_json);
193 resp.set_header("Content-Type", "application/json");
194 });
195 //2.设置http服务器静态路径(逻辑根目录)
196 http_svr_.set_mount_point("/","./web");
197 //3.监听起来
198 http_svr_.listen(svr_ip_.c_str(),svr_port_);
199 }