前言:
首先恭喜我自己!!!第一个项目初步的业务已经圆满结束
不过过程确实很凄惨....我觉得架构实现起来是最难的 往往都是跟着人家一点点码出来架构 然后一点点来回试sql的正确 感觉历经磨难...借助ai的辅助总归是把这个项目的基本业务完成了
我又稍微优化(×细分)了一下架构 给大家展示一下
ser.h
cpp
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<string>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<event.h>
#include<jsoncpp/json/json.h>
#include<mysql/mysql.h>
using namespace std;
const int listenmax=10;
enum op_type{
DL=1,
ZC,
CKYY,
YD,
CKYD,
QXYD,
TC
};
//mysql数据库操作类
class mysql_client{
public:
// 默认构造函数:初始化数据库连接参数(本地数据库默认配置)
mysql_client(){
db_ips="127.0.0.1";
db_username="root";
db_dbname="project_db";
db_passwd="770202";
}
// 析构函数:关闭数据库连接
~mysql_client(){
mysql_close(&mysql_con);
}
bool mysql_connectserver();
// 参数:tel(手机号)、passwd(密码)、name(用户名)
bool mysql_register(const string &tel,const string &passwd,const string &name);
bool mysql_login(const string&tel,const string &passwd,string &name);
// 查看可预约信息:从数据库查询预约数据,存入JSON对象
// 参数:resval(输出参数,存储查询结果)
bool mysql_show_ticket(Json::Value&resval);
bool mysql_subscribe_ticket(int tk_id,string tel);
//查看我的预约,增加函数设计参数
//取消预约,增加函数设计参数
bool mysql_cancel_sub_ticket(int yd_id, const string& tel);
bool mysql_show_sub_ticket(const string& tel, Json::Value& resval);
private:
// 事务开始(用于需要原子性的操作,如预订时减库存+记录预订)
bool mysql_user_begin();
// 事务提交(操作成功时确认变更)
bool mysql_user_commit();
// 事务回滚(操作失败时撤销变更)
bool mysql_user_rollback();
private:
MYSQL mysql_con;
string db_ips;
string db_username;
string db_dbname;
string db_passwd;
};
//服务器监听
class socket_listen{
public:
socket_listen(){
sockfd=-1;
m_port=6000;
m_ips="127.0.0.1";
}
socket_listen(string ips,short port):m_ips(ips),m_port(port){
sockfd=-1;
}
bool socket_init();
int accept_client();
void set_base(struct event_base*base){
this->base=base;
}
int getsockfd()const{
return sockfd;
}
struct event_base* get_base()const{
return base;
}
private:
int sockfd;
short m_port;
string m_ips;
struct event_base * base;
};
//客户端连接处理
class socket_con{
public:
socket_con(int fd):c(fd){
c_ev=NULL;
}
void set_ev(struct event*ev){
c_ev=ev;
}
~socket_con(){
event_free(c_ev);
close(c);
}
void recv_data();
void send_err();
void send_ok();
void user_register();
void user_login();
void user_show_ticket();//查看预约信息
void user_subscribe_ticket();//预订
void user_cancel_sub_ticket();
void user_show_sub_ticket();
private:
int c;
struct event* c_ev;
Json::Value val;
//mysql_client cli;
};
listen.cpp
cpp
#include"ser.h"
bool socket_listen::socket_init(){
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sockfd)return false;
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(m_port);
saddr.sin_addr.s_addr=inet_addr(m_ips.c_str());
int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(-1==res){
perror("绑定失败\n");
close(sockfd);
return false;
}
res=listen(sockfd,listenmax);
if(res==-1)return false;
return true;
}
int socket_listen::accept_client(){
int c=accept(sockfd,NULL,NULL);
return c;
}
ser.cpp
cpp
#include"ser.h"
//mysql_client
//socket_listen
//socket_con
void socket_con::send_err(){
Json::Value res_val;
res_val["status"]="ERR";
send(c,res_val.toStyledString().c_str(),strlen(res_val.toStyledString().c_str()),0);
}
void socket_con::send_ok(){
Json::Value res_val;
res_val["status"]="OK";
send(c,res_val.toStyledString().c_str(),strlen(res_val.toStyledString().c_str()),0);
}
void socket_con::user_register(){
string tel,passwd,username;
tel=val["user_tel"].asString();
passwd=val["user_passwd"].asString();
username=val["user_name"].asString();
if(tel.empty()||passwd.empty()||username.empty()){
send_err();
return;
}
mysql_client cli;
if(!cli.mysql_connectserver()){
send_err();
return;
}
if(!cli.mysql_register(tel,passwd,username)){
send_err();
return;
}
send_ok();
return;
}
void socket_con::user_login(){
string tel=val["user_tel"].asString();
string passwd=val["user_passwd"].asString();
string user_name;
mysql_client cli;
if(!cli.mysql_connectserver()){
send_err();
return;
}
if(!cli.mysql_login(tel,passwd,user_name)){
send_err();
return;
}
Json::Value res_val;
res_val["status"]="OK";
res_val["user_name"]=user_name;
send(c,res_val.toStyledString().c_str(),strlen(res_val.toStyledString().c_str()),0);
return;
}
void socket_con::user_show_ticket(){
Json::Value resval;
mysql_client cli;
if(!cli.mysql_connectserver()){
send_err();
return;
}
if(!cli.mysql_show_ticket(resval)){
send_err();
return;
}
send(c,resval.toStyledString().c_str(),strlen(resval.toStyledString().c_str()),0);
return;
}
void socket_con::user_subscribe_ticket(){
//client -> tk_id ,tel
int tk_id=val["index"].asInt();
string tel=val["tel"].asString();
mysql_client cli;
if(!cli.mysql_connectserver()){
cout<<"connect mysql err"<<endl;
send_err();
return;
}
if(!cli.mysql_subscribe_ticket(tk_id,tel)){
send_err();
return;
}
send_ok();
return;
}
void socket_con::user_cancel_sub_ticket(){
string tel = val["tel"].asString();
int yd_id = val["yd_id"].asInt();
Json::Value resval; // 存储要返回给客户端的结果
mysql_client cli; // 数据库操作实例
if (!cli.mysql_connectserver()) {
send_err();
return;
}
if (!cli.mysql_cancel_sub_ticket(yd_id,tel)) {
send_err();
return;
}
resval["status"] = "OK";
send(c, resval.toStyledString().c_str(), strlen(resval.toStyledString().c_str()), 0);
}
void socket_con::user_show_sub_ticket(){
string tel = val["tel"].asString();
Json::Value resval; // 存储要返回给客户端的结果
mysql_client cli; // 数据库操作实例
if (!cli.mysql_connectserver()) {
send_err();
return;
}
if (!cli.mysql_show_sub_ticket(tel, resval)) {
send_err();
return;
}
send(c, resval.toStyledString().c_str(), strlen(resval.toStyledString().c_str()), 0);
}
void socket_con::recv_data(){
char buff[256]={0};
int n=recv(c,buff,255,0);
if(n<=0){
cout<<"client close"<<endl;
delete this;
return;
}
//测试
cout<<"recv:"<<buff<<endl;
Json::Reader Read;
if(!Read.parse(buff,val)){
cout<<"recv_data:解析json失败"<<endl;
send_err();
return;
}
int ops=val["type"].asInt();
switch(ops){
case DL://登陆
user_login();
break;
case ZC://注册
user_register();
break;
case CKYY://查看预约
user_show_ticket();
break;
case YD://预订
user_subscribe_ticket();
break;
case QXYD://取消预订
user_cancel_sub_ticket();
break;
case CKYD://查看预订
user_show_sub_ticket();
break;
case TC://退出
break;
default:
cout<<"输入无效"<<endl;
break;
}
//解析
}
//callback
void SOCK_CON_CALLBACK(int fd,short ev,void *arg){
socket_con*q=(socket_con*)arg;
if(ev&EV_READ){
q->recv_data();
}
}
void SOCK_LIS_CALLBACK(int sockfd,short ev,void *arg){
socket_listen* p=(socket_listen*)arg;
if(p==NULL)return;
if(ev&EV_READ){ //处理读事件
int c=p->accept_client();
if(c==-1)return;
cout<<"accept:c="<<c<<endl;
socket_con *q=new socket_con(c);
struct event*c_ev=event_new(p->get_base(),c,EV_READ|EV_PERSIST,SOCK_CON_CALLBACK,q);
if(c_ev==NULL){
close(c);
delete q;
return;
}
q->set_ev(c_ev);
//添加到libevent
event_add(c_ev,NULL);
}
}
int main(){
//监听套接字
socket_listen socket_ser;
if(!socket_ser.socket_init()){
cout<<"socket init err!"<<endl;
exit(1);
}
//创建libevent base
struct event_base*base=event_init();
if(base==NULL){
cout<<"base null"<<endl;
exit(1);
}
//设置socket_listen中的libevent中的base
socket_ser.set_base(base);
//添加sockfd到libevent
struct event*sock_ev=event_new(base,socket_ser.getsockfd(),EV_READ|EV_PERSIST,SOCK_LIS_CALLBACK,&socket_ser);
event_add(sock_ev,NULL);
//启动事件循环
event_base_dispatch(base);//select poll epoll
//释放资源
event_free(sock_ev);
event_base_free(base);
return 0;
}
mysqlconnect.cpp
cpp
#include"ser.h"
bool mysql_client::mysql_connectserver(){
MYSQL*mysql=mysql_init(&mysql_con);
if(mysql==NULL)return false;
mysql=mysql_real_connect(mysql,db_ips.c_str(),db_username.c_str(),db_passwd.c_str(),db_dbname.c_str(),3306,NULL,0);
if(mysql==NULL){
cout<<"connect db server err"<<endl;
return false;
}
return true;
}
bool mysql_client::mysql_register(const string &tel,const string &passwd,const string &name){
//insert into user_info values(0,'13200000000','小王','123456',1);
string sql=string("insert into user_info values(0,'")+tel+string("','")+name+string("','")+passwd+string("',1)");
if(mysql_query(&mysql_con,sql.c_str())!=0)return false;
return true;
}
bool mysql_client::mysql_login(const string&tel,const string &passwd,string &name){
//select username,passwd from user_info where tel=15502639510;
string sql1=string("select username,passwd from user_info where tel=")+tel;
if(mysql_query(&mysql_con,sql1.c_str())!=0){
return false;
}
MYSQL_RES*r=mysql_store_result(&mysql_con);
if(r==NULL)return false;
int num=mysql_num_rows(r);//获取结果集有多少行,0行就是为查询到,意味着该用户没有注册
if(num==0){
mysql_free_result(r);
return false;
}
MYSQL_ROW row=mysql_fetch_row(r);
name=row[0];
string password=row[1];
if(password.compare(passwd)!=0)return false;
mysql_free_result(r);
return true;
}
bool mysql_client::mysql_show_ticket(Json::Value &resval){
string sql="select tk_id,addr,max,num,use_date from ticket_info";
if(mysql_query(&mysql_con,sql.c_str())!=0){
cout<<"show ticket err"<<endl;
return false;
}
MYSQL_RES*r=mysql_store_result(&mysql_con);
if(r==NULL)return false;
int n=mysql_num_rows(r);
if(n==0){
resval["status"]="OK";
resval["num"]=0;
return true;
}
resval["status"]="OK";
resval["num"]=n;
for(int i=0;i<n;i++){
MYSQL_ROW row=mysql_fetch_row(r);
Json::Value tmp;
tmp["tk_id"]=row[0];
tmp["add"]=row[1];
tmp["max"]=row[2];
tmp["num"]=row[3];
tmp["use_date"]=row[4];
resval["arr"].append(tmp);
}
return true;
}
bool mysql_client::mysql_user_begin(){
if(mysql_query(&mysql_con,"begin")!=0){
return false;
}
return true;
}
bool mysql_client::mysql_user_commit(){
if(mysql_query(&mysql_con,"commit")!=0){
return false;
}
return true;
}
bool mysql_client::mysql_user_rollback(){
if(mysql_query(&mysql_con,"rollback")!=0){
return false;
}
return true;
}
bool mysql_client::mysql_subscribe_ticket(int tk_id,string tel){
mysql_user_begin();//启动事务
string s1=string("select max,num from ticket_info where tk_id=")+to_string(tk_id);
if(mysql_query(&mysql_con,s1.c_str())!=0){
cout<<"查询max,num失败"<<endl;
mysql_user_rollback();
return false;
}
MYSQL_RES*r=mysql_store_result(&mysql_con);
if(r==NULL){
cout<<"获取结果集失败"<<endl;
mysql_user_rollback();
return false;
}
int Num=mysql_num_rows(r);
if(Num!=1){
cout<<"记录行不为一"<<endl;
mysql_user_rollback();
return false;
}
MYSQL_ROW row=mysql_fetch_row(r);
string str_max=row[0];//总票数
string str_num=row[1];//当前已订票数
int tk_max=atoi(str_max.c_str());
int tk_num=atoi(str_num.c_str());
if(tk_max<=tk_num){
cout<<"没有可用的票"<<endl;
mysql_user_rollback();
return false;
}
tk_num++;
string s2=("update ticket_info set num=")+ to_string(tk_num)+ string(" where tk_id=")+to_string(tk_id);
if(mysql_query(&mysql_con,s2.c_str())!=0){
cout<<"修改预订票数失败"<<endl;
mysql_user_rollback();
return false;
}
//sub_ticket
string s3=string("insert into sub_ticket values(0,")+to_string(tk_id)+string(",'")+tel+string("',now())");
if(mysql_query(&mysql_con,s3.c_str())!=0){
cout<<"存入预订信息失败"<<endl;
mysql_user_rollback();
return false;
}
mysql_user_commit();
return true;
}
bool mysql_client::mysql_cancel_sub_ticket(int yd_id, const string& tel){
mysql_user_begin(); // 开启事务
// 步骤1:查询该预约对应的tk_id(用于更新库存)
string sql_query = "SELECT tk_id FROM sub_ticket WHERE yd_id = " + to_string(yd_id) + " AND tel = '" + tel + "'";
if (mysql_query(&mysql_con, sql_query.c_str()) != 0) {
cout << "查询tk_id失败:" << mysql_error(&mysql_con) << endl;
mysql_user_rollback();
return false;
}
MYSQL_RES* res = mysql_store_result(&mysql_con);
if (res == NULL || mysql_num_rows(res) == 0) {
cout << "未找到该预约记录(yd_id=" << yd_id << ",tel=" << tel << ")" << endl;
mysql_free_result(res);
mysql_user_rollback();
return false;
}
MYSQL_ROW row = mysql_fetch_row(res);
int tk_id = atoi(row[0]); // 获取tk_id
mysql_free_result(res);
// 步骤2:更新ticket_info的库存(num-1)
string sql_update = "UPDATE ticket_info SET num = num - 1 WHERE tk_id = " + to_string(tk_id);
if (mysql_query(&mysql_con, sql_update.c_str()) != 0) {
cout << "更新库存失败:" << mysql_error(&mysql_con) << endl;
mysql_user_rollback();
return false;
}
// 步骤3:删除sub_ticket中对应的预约记录(这一步之前漏掉了!)
string sql_delete = "DELETE FROM sub_ticket WHERE yd_id = " + to_string(yd_id) + " AND tel = '" + tel + "'";
if (mysql_query(&mysql_con, sql_delete.c_str()) != 0) {
cout << "删除预约记录失败:" << mysql_error(&mysql_con) << endl;
mysql_user_rollback(); // 失败则回滚
return false;
}
int affected = mysql_affected_rows(&mysql_con);
if (affected <= 0) {
cout << "未删除任何记录(yd_id不存在或tel不匹配)" << endl;
mysql_user_rollback(); // 回滚之前的库存更新
return false;
}
// 所有步骤成功,提交事务
mysql_user_commit();
return true;
}
bool mysql_client::mysql_show_sub_ticket(const string& tel, Json::Value& resval){
mysql_user_begin();
string sql = "SELECT "
"sub_ticket.yd_id, sub_ticket.tk_id, ticket_info.addr, ticket_info.use_date, sub_ticket.curr_time "
"FROM sub_ticket "
"JOIN ticket_info ON sub_ticket.tk_id = ticket_info.tk_id "
"WHERE sub_ticket.tel = '" + tel + "'"; // 只查当前用户的
if (mysql_query(&mysql_con, sql.c_str()) != 0) {
cout << "查询我的预约失败:" << mysql_error(&mysql_con) << endl;
mysql_user_rollback();
return false;
}
// 处理结果集
MYSQL_RES* r = mysql_store_result(&mysql_con);
if (r == NULL){
mysql_user_rollback();
return false;
}
int num = mysql_num_rows(r); // 该用户的预约总数
resval["status"] = "OK";
resval["num"] = num;
// 遍历结果,存入JSON数组
for (int i = 0; i < num; i++) {
MYSQL_ROW row = mysql_fetch_row(r);
Json::Value tmp;
tmp["yd_id"] = row[0]; // 预约记录ID(sub_ticket.yd_id)
tmp["tk_id"] = row[1]; // 项目编号(ticket_info.tk_id)
tmp["addr"] = row[2]; // 预约地点(ticket_info.addr)
tmp["use_date"] = row[3]; // 可使用日期(ticket_info.use_date)
tmp["curr_time"] = row[4];// 预约时间(sub_ticket.curr_time)
resval["arr"].append(tmp);
}
mysql_free_result(r);
mysql_user_commit();
return true;
}
接下来是客户端的
cli.h
cpp
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<string>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<jsoncpp/json/json.h>
using namespace std;
const int offset=2;
enum op_type{DL=1,ZC,CKYY,YD,CKYD,QXYD,TC};
//客户端及具体实现功能
class socket_client{
public:
socket_client(){
sockfd=-1;
ips="127.0.0.1";
port=6000;
dl_flg=false;
user_op=0;
runing=true;
}
socket_client(string ips,short port){
sockfd=-1;
this->ips=ips;
this->port=port;
dl_flg=false;
user_op=0;
runing=true;
}
void print_info();
~socket_client(){
close(sockfd);
}
bool connect_server();
void user_register();
void user_login();
void user_show_ticket();
void user_subscribe_ticket();
void user_cancel_sub_ticket();
void user_show_sub_ticket();
void run();
private:
string ips;
short port;
int sockfd;
bool dl_flg;
string username;
string usertel;
int user_op;
bool runing;
Json::Value m_val;
};
connect.cpp
cpp
#include"cli.h"
bool socket_client::connect_server(){
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sockfd){
perror("create socket err!\n");
return false;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(port);
saddr.sin_addr.s_addr=inet_addr(ips.c_str());
int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res==-1){
perror("connect ser err!\n");
return false;
}
cout<<"connect to server success"<<endl;
return true;
}
cli.cpp
cpp
#include"cli.h"
void socket_client::print_info(){
if(dl_flg){
cout<<"--已登陆-------用户名:"<<username<<"-------"<<endl;
cout<<"1:查看预约 2:预订 3: 查看我的预约 4:取消预约 5:退出"<<endl;
cout<<"-------------------------------------------------"<<endl;
cout<<"请输入选项编号:"<<endl;
cin>>user_op;
user_op+=offset;
}
else{
cout<<"---未登陆-----游客-----"<<endl;
cout<<"1:登陆 2:注册 3: 退出 "<<endl;
cout<<"-----------------"<<endl;
cout<<"请输入选项编号:"<<endl;
cin>>user_op;
if(user_op==3)user_op=TC;
}
}
void socket_client::user_register(){
cout<<"请输入用户手机号码:"<<endl;
cin>>usertel;
cout<<"请输入用户名"<<endl;
cin>>username;
string passwd,tmp;
cout<<"请输入密码:"<<endl;
cin>>passwd;
cout<<"请再次输入密码"<<endl;
cin>>tmp;
if(usertel.empty()||username.empty()){
cout<<"手机或用户名不能为空"<<endl;
return;
}
if(passwd.compare(tmp)!=0){
cout<<"密码不一致"<<endl;
return;
}
Json::Value val;
val["type"]=ZC;
val["user_tel"]=usertel;
val["user_name"]=username;
val["user_passwd"]=passwd;
send(sockfd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0);
char buff[256]={0};
if(recv(sockfd,buff,255,0)<=0){
cout<<"ser close"<<endl;
return;
}
val.clear();
Json::Reader Read;
if(!Read.parse(buff,val)){
cout<<"json 解析失败"<<endl;
return;
}
string s=val["status"].asString();
if(s.compare("OK")!=0){
cout<<"注册失败"<<endl;
return;
}
dl_flg=true;
cout<<"注册成功"<<endl;
return;
}
void socket_client::user_login(){
string tel,passwd;
cout<<"请输入手机号码"<<endl;
cin>>tel;
cout<<"请输入密码"<<endl;
cin>>passwd;
if(tel.empty()||passwd.empty()){
cout<<"帐号或密码不能为空"<<endl;
return;
}
Json::Value val;
val["type"]=DL;
val["user_tel"]=tel;
val["user_passwd"]=passwd;
send(sockfd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0);
char buff[256]={0};
int n=recv(sockfd,buff,255,0);
if(n<=0){
cout<<"ser close"<<endl;
return;
}
val.clear();
Json::Reader read;
if(!read.parse(buff,val)){
cout<<"解析json失败"<<endl;
return;
}
string st=val["status"].asString();
if(st.compare("OK")!=0){
cout<<"登陆失败"<<endl;
return;
}
dl_flg=true;
username=val["user_name"].asString();
usertel=tel;
cout<<"登陆成功"<<endl;
return;
}
void socket_client::user_show_ticket(){
Json::Value val;
val["type"]=CKYY;
send(sockfd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0);
char buff[4096]={0};
int n=recv(sockfd,buff,4095,0);
//cout<<buff<<endl;
if(n<=0){
cout<<"ser close"<<endl;
return;
}
m_val.clear();
Json::Reader Read;
if(!Read.parse(buff,m_val)){
cout<<"json解析失败"<<endl;
return;
}
string st=m_val["status"].asString();
if(st.compare("OK")!=0){
cout<<"查询预约信息失败"<<endl;
return;
}
int num=m_val["num"].asInt();
if(num==0){
cout<<"没有可预约的信息"<<endl;
return;
}
cout<<"编号 地点名称 总票数 已预订 时间"<<endl;
for(int i=0;i<num;i++){
cout<<"-----------------------------------------------"<<endl;
cout<<"|"<<m_val["arr"][i]["tk_id"].asString()<<" ";
cout<<m_val["arr"][i]["add"].asString()<<" ";
cout<<m_val["arr"][i]["max"].asString()<<" ";
cout<<m_val["arr"][i]["num"].asString()<<" ";
cout<<m_val["arr"][i]["use_date"].asString()<<"|"<<endl;
cout<<"-----------------------------------------------"<<endl;
}
cout<<endl;
}
void socket_client::user_subscribe_ticket(){
user_show_ticket();
cout<<"请输入要预订的编号:"<<endl;
int index=0;
cin>>index;
//index 有效性检查
Json::Value val;
val["type"]=YD;
val["tel"]=usertel;
val["index"]=index;
send(sockfd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0);
char buff[256]={0};
int n=recv(sockfd,buff,255,0);
if(n<=0){
cout<<"ser close"<<endl;
return;
}
val.clear();
Json::Reader read;
if(!read.parse(buff,val)){
cout<<"json 解析失败"<<endl;
return;
}
string st=val["status"].asString();
if(st.compare("OK")!=0){
cout<<"预订失败"<<endl;
return;
}
cout<<"预订成功"<<endl;
return;
}
//取消一个我的预约
void socket_client::user_cancel_sub_ticket(){
user_show_sub_ticket();
cout << "请输入要取消的预约ID:" << endl;
int yd_id = 0;
cin >> yd_id;
// 构建请求JSON
Json::Value val;
val["type"] = QXYD;
val["tel"] = usertel;
val["yd_id"] = yd_id;
send(sockfd, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0);
char buff[256]={0};
int n=recv(sockfd,buff,255,0);
if(n<=0){
cout<<"ser close"<<endl;
return;
}
val.clear();
Json::Reader read;
if(!read.parse(buff,val)){
cout<<"json 解析失败"<<endl;
return;
}
//待处理失败
string status = val["status"].asString();
if (status == "OK") {
cout << "预约取消成功!" << endl;
} else {
// 失败时显示"输入错误"(包含yd_id不存在、无权限等情况)
cout << "输入错误:该预约ID不存在或您无权取消" << endl;
}
return;
}
//显示我的预约
void socket_client::user_show_sub_ticket(){
Json::Value val;
val["type"] = CKYD;
val["tel"] = usertel;
send(sockfd, val.toStyledString().c_str(), strlen(val.toStyledString().c_str()), 0);
// 接收服务器响应
char buff[4096] = {0};
int n = recv(sockfd, buff, 4095, 0);
if (n <= 0) {
cout << "服务器已断开连接" << endl;
return;
}
m_val.clear();
Json::Reader Read;
if (!Read.parse(buff, m_val)) {
cout << "json解析失败" << endl;
return;
}
// 检查查询状态
string st = m_val["status"].asString();
if (st.compare("OK") != 0) {
cout << "查询我的预约失败" << endl;
return;
}
// 显示结果
int num = m_val["num"].asInt();
if (num == 0) {
cout << "您暂无预约记录" << endl;
return;
}
cout << "我的预约记录:" << endl;
cout << "预约ID 项目编号 地点 可使用日期 预约时间" << endl;
for (int i = 0; i < num; i++) {
cout << "-------------------------------------------------------------------" << endl;
cout << "|" << m_val["arr"][i]["yd_id"].asString() << " "; // 预约记录ID(sub_ticket.yd_id)
cout << m_val["arr"][i]["tk_id"].asString() << " "; // 项目编号(ticket_info.tk_id)
cout << m_val["arr"][i]["addr"].asString() << " "; // 预约地点(ticket_info.addr)
cout << m_val["arr"][i]["use_date"].asString() << " "; // 可使用日期(ticket_info.use_date)
cout << m_val["arr"][i]["curr_time"].asString() << "|" << endl;// 预约时间(sub_ticket.curr_time)
cout << "-------------------------------------------------------------------" << endl;
}
cout << endl;
}
void socket_client::run(){
while(runing){
print_info();
switch(user_op){
case DL:
user_login();
break;
case ZC:
user_register();
break;
case CKYY://查看预约
user_show_ticket();
break;
case YD://预订
user_subscribe_ticket();
break;
case QXYD://取消预订
user_cancel_sub_ticket();
break;
case CKYD://查看预订
user_show_sub_ticket();
break;
case TC://退出
runing=false;
break;
default:
cout<<"输入无效"<<endl;
break;
}
}
}
int main(){
socket_client cli;
if(!cli.connect_server())exit(1);
cli.run();
exit(0);
}
可继续优化点:
🔴 高优先级(安全与稳定性)
1. 数据完整性问题
· send/recv数据完整性
· 实现send_all()和recv_all()函数
· 处理部分发送/接收的情况
· 考虑定义应用层协议(长度头 + JSON体)
2. SQL注入防护
· 预处理语句替换字符串拼接
· 注册功能:mysql_register()
· 登录功能:mysql_login()
· 票务查询:mysql_show_ticket()
· 预订功能:mysql_subscribe_ticket()
3. 输入验证完善
· 空值检查后立即返回(已发现的问题)
· 手机号格式验证(正则表达式)
· 用户名长度限制
· 整数参数范围检查
4. 资源泄漏修复
· 数据库连接管理
· 当前:每次操作新建连接(性能差)
· 目标:实现连接池
· 内存泄漏检查
· socket_con对象的生命周期管理
· MySQL结果集释放mysql_free_result()
🟡 中优先级(性能与用户体验)
5. 非阻塞IO改造
· 服务端socket设为非阻塞
· 客户端socket设为非阻塞
· 处理EAGAIN/EWOULDBLOCK错误码
· 实现IO多路复用的完整优势
6. Libevent性能优化
· 升级到Libevent2新API
· 启用ET边缘触发模式
· 配置高性能事件驱动后端
7. 数据库优化
· 连接池实现
· 为常用查询字段添加索引
· 查询结果缓存机制
8. 错误信息细化
· 区分不同错误类型:
· 用户已存在
· 密码错误
· 票已售完
· 数据库连接失败
· 返回具体错误码和描述
🟢 低优先级(代码质量与扩展性)
9. 代码结构优化
· 配置文件外部化
· 数据库连接参数
· 服务器端口配置
· 日志系统完善
· 统一错误处理机制
10. 安全增强
· 密码加密传输(MD5/SHA1)
· 敏感信息脱敏
· 请求频率限制
11. 协议优化
· 二进制协议替代JSON(可选)
· 数据压缩
· 心跳包机制
📋 具体实施计划
Phase 1:安全与稳定性(预计2-3天)
**```
- 数据完整性 → 2. SQL注入防护 → 3. 输入验证 → 4. 资源泄漏
```**
Phase 2:性能优化(预计3-4天)
```
5. 非阻塞IO → 6. Libevent优化 → 7. 数据库连接池
```
Phase 3:用户体验(预计1-2天)
```
8. 错误信息细化 → 9. 配置外部化
```
业务逻辑优化
· 重复查询优化:user_subscribe_ticket()中先调用了user_show_ticket()
· 事务边界细化:某些查询操作不需要事务
· 数据验证前置:客户端先做基础验证,减少服务端压力
客户端体验
· 输入错误处理:类型转换异常处理
· 操作确认:重要操作前的确认提示
· 结果展示优化:表格格式化输出
总结:
sql注入的风险还是很大的 所以有时间需要修改一下那里。让ai预测了一下并发数量 它预测支持几十个让我有点破防 但是我压力测试了一下发现能达到一千多(甚至是因为mysql的连接量满的原因)所以还是超过我的预期的 遇到的困难很多哎 比如sql语句的描述 json的序列化反序列 还有n多的参数记不住。。。总而言之ai帮了大忙(
哎简单来说毕竟这是从0到1的跨越嘛~再多做几个玩具就能游龙了吧(应该? 第一阶段的预约系统就到这里啦~