SQLite 是一个开源的嵌入式关系型数据库管理系统。它不是作为一个独立进程运行的服务,而是作为一个库直接链接到应用程序中。这意味着使用 SQLite 的应用程序可以随身携带其数据库,并且可以在不需要单独服务器管理的情况下操作数据。
打开官网界面
https://www.sqlite.org/download.html
下载对应平台的源代码
解压缩这四个文件到你想要引入的项目根目录
例如:
如果不考虑防止SQL注入以及加密之类的
其实只需要包含Sqlite3.h和Sqlite3.c两个文件在项目内就够了
// 1. 打开数据库 或者创建数据库
sqlite3* db = nullptr;
int ret = sqlite3_open("test.db", &db);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_open error 打开文件失败" << std::endl;
return -1;
}
// 2. 创建表
const char* sql = "create table if not exists user(id integer primary key autoincrement, name text, age integer)";
char* errmsg = nullptr;
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: " << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
打开数据库 或者创建数据库用sqlite3_open
在执行sql语句不需要返回结果集时用sqlite3_exec
int sqlite3_exec(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
void (*callback)(void*, int, char**, char**), /* Callback function */
void *pArg, /* Argument passed to callback */
char **pzErrmsg /* Error msg written here */
);
参数说明如下:
函数返回值:
回调函数的原型如下:
需要返回结果集时一般用sqlite3_prepare_v2() 和 sqlite3_step()和sqlite3_finalize()结合使用
这里就不提供详细解释了,只是提供一个使用例,根据用户姓名和密码搜索用户ID
int DBOperate::GetUserID(const char* name, const char* pwd)
{
std::stringstream sql;
sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";
char* errMsg = nullptr;
sqlite3_stmt* stmtUser;
int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
if (rc != SQLITE_OK) {
std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
return -1; // Return an error code or value indicating failure
}
int userId = -1;
if (sqlite3_step(stmtUser) == SQLITE_ROW) {
userId = sqlite3_column_int(stmtUser, 0);
}
else {
std::cerr << "No matching user found." << std::endl;
}
sqlite3_finalize(stmtUser);
return userId;
}
以下使用例均建立在sqlite3_exec的使用下
// 3. 插入数据
sql = "insert into user(name, age) values('张三', 18)";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 插入数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 5.删除数据
sql = "delete from user where age < 20";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 删除数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 4.更新数据
sql = "update user set age = 20 where name = '张三'";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 更新数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
当我们想要把数据打印出来时,就需要使用sqlite3_exec里的回调函数参数了
int select_callback(void* ptr, int count, char** row, char** rowName)
{
std::vector<std::string>* pv = (std::vector<std::string> *) ptr;
std::string str = "";
for (int i = 0; i < count; ++i)
{
// std::cout << row[i] << " = " << rowName[i] << std::endl;
str += row[i];
str += " = ";
str += rowName[i];
str += "|||";
}
pv->push_back(str);
return 0;
}
sql = "select * from user";
std::vector<std::string> v;
ret = sqlite3_exec(db, sql, select_callback, &v, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error 查询数据失败" << errmsg << std::endl;
return -1;
}
for (auto& s : v)
{
std::cout << s << std::endl;
}
#include <iostream>
#include "sqlite3.h"
#include <vector>
#include <string>
int select_callback(void* ptr, int count, char** row, char** rowName)
{
std::vector<std::string>* pv = (std::vector<std::string> *) ptr;
std::string str = "";
for (int i = 0; i < count; ++i)
{
// std::cout << row[i] << " = " << rowName[i] << std::endl;
str += row[i];
str += " = ";
str += rowName[i];
str += "|||";
}
pv->push_back(str);
return 0;
}
int main()
{
// sqlite3是一个轻量级的数据库,不需要安装,只需要包含头文件即可
// 1. 打开数据库 或者创建数据库
sqlite3* db = nullptr;
int ret = sqlite3_open("test.db", &db);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_open error 打开文件失败" << std::endl;
return -1;
}
// 2. 创建表
const char* sql = "create table if not exists user(id integer primary key autoincrement, name text, age integer)";
char* errmsg = nullptr;
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: " << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 3. 插入数据
sql = "insert into user(name, age) values('张三', 18)";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 插入数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 4.更新数据
sql = "update user set age = 20 where name = '张三'";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 更新数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 5.删除数据
sql = "delete from user where age < 20";
ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error: 删除数据失败" << errmsg << std::endl;
sqlite3_free(errmsg); // 释放错误消息内存
return -1;
}
// 6. 查询数据
sql = "select * from user";
std::vector<std::string> v;
ret = sqlite3_exec(db, sql, select_callback, &v, &errmsg);
if (ret != SQLITE_OK)
{
std::cout << "sqlite3_exec error 查询数据失败" << errmsg << std::endl;
return -1;
}
for (auto& s : v)
{
std::cout << s << std::endl;
}
// 5. 关闭数据库
sqlite3_close(db);
return 0;
}
新建一个单例模式下的数据库类
关于单例模式的讲解可以看我之前的这篇文章
https://cloud.tencent.com/developer/article/2439703
#pragma once
#include "sqlite3.h"
#include <iostream>
#include <string>
struct sqlite3;
struct sqlite3_stmt;
class DBOperate
{
private:
//数据库文件存储路径
std::string _path;
// 打开数据库或者创造数据库
sqlite3* _db;
//检查是否初始化过
bool _isInitialized;
public:
static DBOperate& GetInstance();
void Init(std::string path);
public:
bool DBRegister(const char* name, const char* pwd);
bool DBLogin(const char* name, const char* pwd);
int GetUserID(const char* name, const char* pwd);
private:
DBOperate() : _db(nullptr), _isInitialized(false){};
DBOperate(DBOperate const&) = delete;
void operator=(DBOperate const&) = delete;
~DBOperate()
{
if (_db)
sqlite3_close(_db);
_db = nullptr;
}
};
#include "DBOperate.h"
DBOperate& DBOperate::GetInstance()
{
static DBOperate instance;
return instance;
}
void DBOperate::Init(std::string path)
{
if (_isInitialized)
return;
//数据库文件存储路径
_path = path;
// Open the database
int rc = sqlite3_open(_path.c_str(), &_db);
if (rc != SQLITE_OK) {
std::cerr << "Can't open database: " << sqlite3_errmsg(_db) << std::endl;
return;
}
// Create users table
const char* sqlCreateUsersTable =
"CREATE TABLE IF NOT EXISTS Users ("
"UserID INTEGER PRIMARY KEY AUTOINCREMENT,"
"Username TEXT UNIQUE NOT NULL,"
"Password TEXT NOT NULL"
");";
// Create tokens table
const char* sqlCreateTokensTable =
"CREATE TABLE IF NOT EXISTS Tokens ("
"UserID INTEGER NOT NULL,"
"Token TEXT NOT NULL UNIQUE"
");";
// Execute SQL statements
char* errMsg = nullptr;
rc = sqlite3_exec(_db, sqlCreateUsersTable, nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "SQL error: " << errMsg << std::endl;
sqlite3_free(errMsg);
return;
}
rc = sqlite3_exec(_db, sqlCreateTokensTable, nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "SQL error: " << errMsg << std::endl;
sqlite3_free(errMsg);
return;
}
_isInitialized = true;
}
bool DBOperate::DBRegister(const char* name, const char* pwd)
{
// Prepare and execute the INSERT statement
std::stringstream sql;
sql << "INSERT INTO Users (Username, Password) VALUES ('" << name << "', '" << pwd << "');";
char* errMsg = nullptr;
int rc = sqlite3_exec(_db, sql.str().c_str(), nullptr, nullptr, &errMsg);
if (rc != SQLITE_OK) {
std::cerr << "Failed to register user: " << errMsg << std::endl;
sqlite3_free(errMsg);
return false;
}
return true;
}
bool DBOperate::DBLogin(const char* name, const char* pwd)
{
std::stringstream sql;
sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";
char* errMsg = nullptr;
sqlite3_stmt* stmtUser;
int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
if (rc != SQLITE_OK) {
std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
return false;
}
return true;
}
int DBOperate::GetUserID(const char* name, const char* pwd)
{
std::stringstream sql;
sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";
char* errMsg = nullptr;
sqlite3_stmt* stmtUser;
int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
if (rc != SQLITE_OK) {
std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
return -1; // Return an error code or value indicating failure
}
int userId = -1;
if (sqlite3_step(stmtUser) == SQLITE_ROW) {
userId = sqlite3_column_int(stmtUser, 0);
}
else {
std::cerr << "No matching user found." << std::endl;
}
sqlite3_finalize(stmtUser);
return userId;
}
在需要引入数据库交互的.cpp文件里包含数据库类的所在头文件
服务端完整的TcpSocket类代码我就不放了,这里采用cJSON进行CS之间的数据交互
cJSON在CS之间的数据交互可以参考我写的这篇文章
cJSON使用介绍以及如何利用cJSON在服务端和客户端中进行数据传输
void TcpSocket::SC_LoginRespond(cJSON* root)
{
cJSON* username = cJSON_GetObjectItem(root, "username");
cJSON* password = cJSON_GetObjectItem(root, "password");
if (username == nullptr || username->type != cJSON_String && password == nullptr || password->type != cJSON_String)
{
Close();
return;
}
cJSON* sc_login = cJSON_CreateObject();
std::string strUsername = username->valuestring;
std::string strPassword = password->valuestring;
bool loginFlag = DBOperate::GetInstance().DBLogin(strUsername.c_str(), strPassword.c_str());
if (loginFlag)
{
// 用户名密码正确,登录成功
cJSON_AddStringToObject(sc_login, "cmd", "SC_Login");
cJSON_AddNumberToObject(sc_login, "result", 1);
cJSON_AddStringToObject(sc_login, "msg", "Login Success");
cJSON* player = cJSON_CreateObject();
std::string token = DBOperate::GetInstance().DBGetToken(_userId);
cJSON_AddNumberToObject(player, "playerId", _userId);
cJSON_AddStringToObject(player, "token", token.c_str());
cJSON_AddStringToObject(player, "username", username->valuestring);
cJSON_AddStringToObject(player, "password", password->valuestring);
cJSON_AddItemToObject(sc_login, "player", player);
}
else
{
// 用户名或者密码错误
cJSON_AddStringToObject(sc_login, "cmd", "SC_Login");
cJSON_AddNumberToObject(sc_login, "result", 0);
cJSON_AddStringToObject(sc_login, "msg", "User Or Password Failed");
}
std::string msg = cJSON_Print(sc_login);
cJSON_Delete(sc_login);
SendData(msg.c_str(), msg.size());
}
void TcpSocket::SC_RegisterRespond(cJSON* root)
{
//cJSON *nike = cJSON_GetObjectItem(root, "nike");
//cJSON *username = cJSON_GetObjectItem(root, "username");
cJSON* username = cJSON_GetObjectItem(root, "username");
cJSON* password = cJSON_GetObjectItem(root, "password");
// password username
if (username == nullptr || password == nullptr ||
username->type != cJSON_String || password->type != cJSON_String)
{
Close();
return;
}
cJSON* sc_register = cJSON_CreateObject();
//验证是否注册成功
bool registerFlag = DBOperate::GetInstance().DBRegister(username->valuestring, password->valuestring);
if (registerFlag)
{
cJSON_AddStringToObject(sc_register, "cmd", "SC_Register");
cJSON_AddStringToObject(sc_register, "username", username->valuestring);
cJSON_AddStringToObject(sc_register, "password", password->valuestring);
cJSON_AddNumberToObject(sc_register, "result", 1);
cJSON_AddStringToObject(sc_register, "msg", "Register Success");
}
else
{
// 用户名已存在,注册失败
cJSON_AddStringToObject(sc_register, "cmd", "SC_RegisterFailed");
cJSON_AddStringToObject(sc_register, "msg", "Register Failed");
}
char* msg = cJSON_Print(sc_register);
cJSON_Delete(sc_register);
SendData(msg, strlen(msg));
free(msg);
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。