一,数据库sqlite3学习
数据库命令:
1)系统命令 , 都以'.'开头
.exit
.quit
.table 查看表
.schema 查看表的结构
2)sql语句, 都以‘;’结尾
1-- 创建一张表
create table stuinfo(id integer, name text, age integer, score float);
2-- 插入一条记录
insert into stuinfo values(1001, 'zhangsan', 18, 80);
insert into stuinfo (id, name, score) values(1002, 'lisi', 90);
3-- 查看数据库记录
select * from stuinfo;
select * from stuinfo where score = 80;
select * from stuinfo where score = 80 and name= 'zhangsan';
select * from stuinfo where score = 80 or name='wangwu';
select name,score from stuinfo; 查询指定的字段
select * from stuinfo where score >= 85 and score < 90;
4-- 删除一条记录
delete from stuinfo where id=1003 and name='zhangsan';
5-- 更新一条记录
update stuinfo set age=20 where id=1003;
update stuinfo set age=30, score = 82 where id=1003;
6-- 删除一张表
drop table stuinfo;
7-- 增加一列
alter table stuinfo add column sex char;
8-- 删除一列
create table stu as select id, name, score from stuinfo;
drop table stuinfo;
alter table stu rename to stuinfo;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#define DATABASE "student.db" // 定义数据库名称
#define N 128 // 定义字符数组大小
// 插入记录函数
int do_insert(sqlite3 *db) {
int id;
char name[32] = {};
char sex;
int score;
char sql[N] = {};
char *errmsg;
printf("Input id:");
scanf("%d", &id); // 输入学生ID
printf("Input name:");
scanf("%s", name); // 输入学生姓名
getchar(); // 读取换行符
printf("Input sex:");
scanf("%c", &sex); // 输入学生性别
printf("Input score:");
scanf("%d", &score); // 输入学生成绩
sprintf(sql, "insert into stu values(%d, '%s', '%c', %d)", id, name, sex, score); // 构建SQL插入语句
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { // 执行SQL语句
printf("%s\n", errmsg); // 输出错误信息
} else {
printf("Insert done.\n"); // 插入成功提示
}
return 0;
}
// 删除记录函数
int do_delete(sqlite3 *db) {
int id;
char sql[N] = {};
char *errmsg;
printf("Input id:");
scanf("%d", &id); // 输入要删除的学生ID
sprintf(sql, "delete from stu where id = %d", id); // 构建SQL删除语句
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { // 执行SQL语句
printf("%s\n", errmsg); // 输出错误信息
} else {
printf("Delete done.\n"); // 删除成功提示
}
return 0;
}
// 更新记录函数
int do_update(sqlite3 *db) {
int id;
char sql[N] = {};
char name[32] = "zhangsan";
char *errmsg;
printf("Input id:");
scanf("%d", &id); // 输入要更新的学生ID
sprintf(sql, "update stu set name='%s' where id=%d", name, id); // 构建SQL更新语句
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) { // 执行SQL语句
printf("%s\n", errmsg); // 输出错误信息
} else {
printf("update done.\n"); // 更新成功提示
}
return 0;
}
// 查询回调函数
int callback(void *arg, int f_num, char ** f_value, char ** f_name) {
int i = 0;
for(i = 0; i < f_num; i++) {
printf("%-8s", f_value[i]); // 输出查询结果
}
printf("++++++++++++++++++++++");
putchar(10);
return 0;
}
// 查询记录函数
int do_query(sqlite3 *db) {
char *errmsg;
char sql[N] = "select count(*) from stu where name='zhangsan';"; // 查询name为zhangsan的记录数量
if(sqlite3_exec(db, sql, callback, NULL, &errmsg) != SQLITE_OK) { // 执行SQL语句
printf("%s", errmsg); // 输出错误信息
} else {
printf("select done.\n"); // 查询成功提示
}
}
// 另一种查询记录的函数
int do_query1(sqlite3 *db) {
char *errmsg;
char ** resultp;
int nrow;
int ncolumn;
if(sqlite3_get_table(db, "select * from stu", &resultp, &nrow, &ncolumn, &errmsg) != SQLITE_OK) { // 获取查询结果表
printf("%s\n", errmsg); // 输出错误信息
return -1;
} else {
printf("query done.\n"); // 查询成功提示
}
int i = 0;
int j = 0;
int index = ncolumn;
for(j = 0; j < ncolumn; j++) {
printf("%-10s ", resultp[j]); // 输出列名
}
putchar(10);
for(i = 0; i < nrow; i++) {
for(j = 0; j < ncolumn; j++) {
printf("%-10s ", resultp[index++]); // 输出查询结果
}
putchar(10);
}
return 0;
}
// 主函数
int main(int argc, const char *argv[]) {
sqlite3 *db;
char *errmsg;
int n;
if(sqlite3_open(DATABASE, &db) != SQLITE_OK) { // 打开数据库
printf("%s\n", sqlite3_errmsg(db)); // 输出错误信息
return -1;
} else {
printf("open DATABASE success.\n"); // 打开数据库成功提示
}
if(sqlite3_exec(db, "create table if not exists stu(id int, name char , sex char , score int);",
NULL, NULL, &errmsg) != SQLITE_OK) { // 创建表
printf("%s\n", errmsg); // 输出错误信息
} else {
printf("Create or open table success.\n"); // 创建表成功提示
}
while(1) {
printf("********************************************\n");
printf("1: insert 2:query 3:delete 4:update 5:quit\n");
printf("********************************************\n");
printf("Please select:");
scanf("%d", &n); // 输入选择项
switch(n) {
case 1:
do_insert(db); // 插入记录
break;
case 2:
do_query(db); // 查询记录
// do_query1(db); // 另一种查询方式
break;
case 3:
do_delete(db); // 删除记录
break;
case 4:
do_update(db); // 更新记录
break;
case 5:
printf("main exit.\n");
sqlite3_close(db); // 关闭数据库
exit(0); // 退出程序
break;
default :
printf("Invalid data n.\n"); // 无效输入提示
}
}
return 0;
}
运行效果
二,在线词典项目
实现一个基于TCP协议的字典查询系统,具备用户注册、登录、单词查询和历史记录查询的功能。
服务器端逻辑:
1. 启动和初始化:
服务器启动时,创建一个TCP套接字,用于监听客户端的连接请求。
服务器绑定到指定的IP地址和端口号,以便客户端可以连接。
服务器进入监听模式,准备接受多个客户端的连接请求。
2. 处理连接:
服务器通过调用`accept`函数接受客户端的连接请求。
每当有新的客户端连接时,服务器创建一个子进程(通过`fork`),由子进程负责处理该客户端的所有请求,父进程继续监听新的连接。
通过设置信号处理函数`SIGCHLD`,父进程可以避免僵尸进程的产生。
3. 数据库操作:
服务器使用SQLite数据库存储用户信息和查询历史记录。
在服务器启动时,打开指定的SQLite数据库文件。
根据客户端的请求类型(注册、登录、查询单词、查询历史记录),服务器执行相应的SQL语句对数据库进行操作。
4. 请求处理:
注册(R):服务器从客户端接收注册请求,插入新的用户记录到数据库中。如果用户名已存在,返回错误信息,否则返回注册成功信息。
登录(L):服务器接收登录请求,查询数据库验证用户名和密码。如果验证成功,返回登录成功信息,否则返回错误信息。
查询单词(Q):服务器接收单词查询请求,从本地字典文件中查找单词的定义。如果找到单词,将定义返回给客户端,并记录查询历史;如果未找到,返回未找到的信息。
查询历史记录(H):服务器接收查询历史记录请求,从数据库中查询该用户的所有查询记录,并逐条返回给客户端。
客户端逻辑:
1. 启动和连接:
客户端启动时,创建一个TCP套接字,并连接到服务器的指定IP地址和端口号。
2. 用户交互:
客户端通过命令行界面与用户交互,提供注册、登录、查询单词和查询历史记录的功能菜单。
用户选择相应的功能后,客户端根据用户输入构建消息结构体,并发送到服务器。
3. 请求发送和接收:
注册:用户选择注册功能后,输入用户名和密码,客户端将这些信息发送到服务器,并接收服务器的响应信息(成功或失败)。
登录:用户选择登录功能后,输入用户名和密码,客户端将这些信息发送到服务器,并接收服务器的响应信息。如果登录成功,客户端进入下一级菜单。
查询单词:用户选择查询单词功能后,输入要查询的单词,客户端将单词发送到服务器,并接收服务器返回的单词定义。
查询历史记录:用户选择查询历史记录功能后,客户端发送请求到服务器,并接收服务器返回的历史记录信息。
具体的交互过程:
1. 启动服务器:
服务器启动,绑定到指定的IP和端口,并进入监听模式。
2. 客户端连接:
客户端启动后,连接到服务器。
服务器接受客户端的连接请求,创建子进程处理该客户端。
3. 用户操作:
用户在客户端选择注册、登录、查询单词或查询历史记录。
客户端根据用户选择构建消息,发送到服务器。
4. 服务器处理:
服务器子进程接收客户端的请求,根据请求类型进行相应的数据库操作或文件操作,并构建响应消息发送回客户端。
5. 客户端接收响应:
客户端接收服务器的响应信息,并在命令行界面显示结果。
6. 循环处理:
用户可以多次执行不同的操作,客户端和服务器保持通信,直到用户选择退出。
客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#define N 32 // 定义用户名的最大长度
#define R 1 // 定义操作类型为注册
#define L 2 // 定义操作类型为登录
#define Q 3 // 定义操作类型为查询
#define H 4 // 定义操作类型为历史记录
// 定义通信双方的信息结构体
typedef struct {
int type; // 操作类型
char name[N]; // 用户名
char data[256]; // 数据(如密码或查询的单词等)
}MSG;
// 注册函数
int do_register(int sockfd, MSG *msg)
{
msg->type = R; // 设置操作类型为注册
printf("Input name:"); // 提示输入用户名
scanf("%s", msg->name); // 获取用户名并存储到msg结构体中
getchar(); // 读取并丢弃多余的换行符
printf("Input passwd:"); // 提示输入密码
scanf("%s", msg->data); // 获取密码并存储到msg结构体中
if(send(sockfd, msg, sizeof(MSG),0) < 0) // 将msg发送给服务器
{
printf("fail to send.\n"); // 发送失败提示
return -1; // 返回错误
}
if(recv(sockfd, msg, sizeof(MSG), 0) < 0) // 从服务器接收响应
{
printf("Fail to recv.\n"); // 接收失败提示
return -1; // 返回错误
}
// 输出服务器返回的信息(注册成功或用户已存在)
printf("%s\n", msg->data);
return 0; // 返回成功
}
// 登录函数
int do_login(int sockfd, MSG *msg)
{
msg->type = L; // 设置操作类型为登录
printf("Input name:"); // 提示输入用户名
scanf("%s", msg->name); // 获取用户名并存储到msg结构体中
getchar(); // 读取并丢弃多余的换行符
printf("Input passwd:"); // 提示输入密码
scanf("%s", msg->data); // 获取密码并存储到msg结构体中
if(send(sockfd, msg, sizeof(MSG),0) < 0) // 将msg发送给服务器
{
printf("fail to send.\n"); // 发送失败提示
return -1; // 返回错误
}
if(recv(sockfd, msg, sizeof(MSG), 0) < 0) // 从服务器接收响应
{
printf("Fail to recv.\n"); // 接收失败提示
return -1; // 返回错误
}
if(strncmp(msg->data, "OK", 3) == 0) // 如果服务器返回"OK",表示登录成功
{
printf("Login ok!\n"); // 输出登录成功信息
return 1; // 返回成功
}
else
{
printf("%s\n", msg->data); // 输出服务器返回的错误信息
}
return 0; // 返回失败
}
// 查询函数
int do_query(int sockfd, MSG *msg)
{
msg->type = Q; // 设置操作类型为查询
puts("--------------");
while(1)
{
printf("Input word:"); // 提示输入查询单词
scanf("%s", msg->data); // 获取单词并存储到msg结构体中
getchar(); // 读取并丢弃多余的换行符
// 客户端,输入#号,返回到上一级菜单
if(strncmp(msg->data, "#", 1) == 0)
break;
// 将要查询的单词发送给服务器
if(send(sockfd,msg, sizeof(MSG), 0) < 0)
{
printf("Fail to send.\n"); // 发送失败提示
return -1; // 返回错误
}
// 等待接受服务器,传递回来的单词的注释信息
if(recv(sockfd, msg,sizeof(MSG), 0) < 0)
{
printf("Fail to recv.\n"); // 接收失败提示
return -1; // 返回错误
}
printf("%s\n", msg->data); // 输出服务器返回的单词注释
}
return 0; // 返回成功
}
// 历史记录查询函数
int do_history(int sockfd, MSG *msg)
{
msg->type = H; // 设置操作类型为历史记录查询
send(sockfd, msg, sizeof(MSG), 0); // 将msg发送给服务器
// 接受服务器传递回来的历史记录信息
while(1)
{
recv(sockfd, msg, sizeof(MSG), 0); // 接收历史记录
if(msg->data[0] == '\0') // 如果接收到的历史记录为空
break;
// 输出历史记录信息
printf("%s\n", msg->data);
}
return 0; // 返回成功
}
// 主函数,客户端入口
int main(int argc, const char *argv[])
{
int sockfd; // 套接字描述符
struct sockaddr_in serveraddr; // 服务器地址结构体
int n; // 菜单选择变量
MSG msg; // 信息结构体
if(argc != 3) // 检查命令行参数个数是否正确
{
printf("Usage:%s serverip port.\n", argv[0]); // 提示正确的使用方法
return -1; // 返回错误
}
if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0) // 创建套接字
{
perror("fail to socket.\n"); // 套接字创建失败提示
return -1; // 返回错误
}
bzero(&serveraddr, sizeof(serveraddr)); // 将服务器地址结构体清零
serveraddr.sin_family = AF_INET; // 设置地址族为IPv4
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址
serveraddr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号
if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) // 连接服务器
{
perror("fail to connect"); // 连接失败提示
return -1; // 返回错误
}
while(1) // 循环显示主菜单
{
printf("*****************************************************************\n");
printf("* 1.register 2.login 3.quit *\n");
printf("*****************************************************************\n");
printf("Please choose:"); // 提示用户选择操作
scanf("%d", &n); // 获取用户选择
getchar(); // 读取并丢弃多余的换行符
switch(n) // 根据用户选择执行相应操作
{
case 1:
do_register(sockfd, &msg); // 注册
break;
case 2:
if(do_login(sockfd, &msg) == 1) // 登录
{
goto next; // 登录成功,跳转到下一级菜单
}
break;
case 3:
close(sockfd); // 关闭套接字
exit(0); // 退出程序
break;
default:
printf("Invalid data cmd.\n"); // 无效的命令提示
}
}
next: // 下一级菜单
while(1) // 循环显示下一级菜单
{
printf("*****************************************************\n");
printf("* 1.query_word 2.history_record 3.quit *\n");
printf("*****************************************************\n");
printf("Please choose:"); // 提示用户选择操作
scanf("%d", &n); // 获取用户选择
getchar(); // 读取并丢弃多余的换行符
switch(n) // 根据用户选择执行相应操作
{
case 1:
do_query(sockfd, &msg); // 查询单词
break;
case 2:
do_history(sockfd, &msg); // 查询历史记录
break;
case 3:
close(sockfd); // 关闭套接字
exit(0); // 退出程序
break;
default :
printf("Invalid data cmd.\n"); // 无效的命令提示
}
}
return 0; // 返回成功
}
服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#define N 32 // 定义用户名的最大长度
#define R 1 // 定义操作类型为注册
#define L 2 // 定义操作类型为登录
#define Q 3 // 定义操作类型为查询
#define H 4 // 定义操作类型为历史记录
#define DATABASE "my.db" // 数据库文件名
// 定义通信双方的信息结构体
typedef struct {
int type; // 操作类型
char name[N]; // 用户名
char data[256]; // 数据(如密码或查询的单词等)
} MSG;
// 函数声明
int do_client(int acceptfd, sqlite3 *db);
void do_register(int acceptfd, MSG *msg, sqlite3 *db);
int do_login(int acceptfd, MSG *msg, sqlite3 *db);
int do_query(int acceptfd, MSG *msg, sqlite3 *db);
int do_history(int acceptfd, MSG *msg, sqlite3 *db);
int history_callback(void* arg, int f_num, char** f_value, char** f_name);
int do_searchword(int acceptfd, MSG *msg, char word[]);
int get_date(char *date);
// ./server 192.168.116.131 6000
int main(int argc, const char *argv[])
{
int sockfd; // 套接字描述符
struct sockaddr_in serveraddr; // 服务器地址结构体
int n; // 菜单选择变量
MSG msg; // 信息结构体
sqlite3 *db; // 数据库指针
int acceptfd; // 接收连接的套接字描述符
pid_t pid; // 进程ID
if(argc != 3) // 检查命令行参数个数是否正确
{
printf("Usage:%s serverip port.\n", argv[0]); // 提示正确的使用方法
return -1; // 返回错误
}
// 打开数据库
if(sqlite3_open(DATABASE, &db) != SQLITE_OK)
{
printf("%s\n", sqlite3_errmsg(db)); // 打开数据库失败提示
return -1; // 返回错误
}
else
{
printf("open DATABASE success.\n"); // 打开数据库成功提示
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) // 创建套接字
{
perror("fail to socket.\n"); // 套接字创建失败提示
return -1; // 返回错误
}
bzero(&serveraddr, sizeof(serveraddr)); // 将服务器地址结构体清零
serveraddr.sin_family = AF_INET; // 设置地址族为IPv4
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址
serveraddr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) // 绑定套接字
{
perror("fail to bind.\n"); // 绑定失败提示
return -1; // 返回错误
}
// 将套接字设为监听模式
if(listen(sockfd, 5) < 0)
{
printf("fail to listen.\n"); // 监听失败提示
return -1; // 返回错误
}
// 处理僵尸进程
signal(SIGCHLD, SIG_IGN);
while(1) // 循环接受客户端连接
{
if((acceptfd = accept(sockfd, NULL, NULL)) < 0) // 接受客户端连接
{
perror("fail to accept"); // 接受连接失败提示
return -1; // 返回错误
}
if((pid = fork()) < 0) // 创建子进程
{
perror("fail to fork"); // 创建子进程失败提示
return -1; // 返回错误
}
else if(pid == 0) // 子进程
{
// 处理客户端具体的消息
close(sockfd); // 关闭父进程的套接字
do_client(acceptfd, db); // 处理客户端请求
}
else // 父进程
{
close(acceptfd); // 关闭子进程的套接字
}
}
return 0; // 返回成功
}
// 处理客户端请求函数
int do_client(int acceptfd, sqlite3 *db)
{
MSG msg; // 信息结构体
while(recv(acceptfd, &msg, sizeof(msg), 0) > 0) // 接收客户端消息
{
printf("type:%d\n", msg.type); // 打印接收到的消息类型
switch(msg.type) // 根据消息类型进行处理
{
case R: // 注册
do_register(acceptfd, &msg, db);
break;
case L: // 登录
do_login(acceptfd, &msg, db);
break;
case Q: // 查询
do_query(acceptfd, &msg, db);
break;
case H: // 历史记录查询
do_history(acceptfd, &msg, db);
break;
default: // 无效的消息类型
printf("Invalid data msg.\n");
}
}
printf("client exit.\n"); // 客户端退出提示
close(acceptfd); // 关闭与客户端的连接
exit(0); // 退出子进程
return 0; // 返回成功
}
// 处理注册请求函数
void do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
char *errmsg; // 错误信息指针
char sql[512]; // SQL语句
snprintf(sql, sizeof(sql), "insert into usr values('%s', '%s');", msg->name, msg->data); // 构造插入用户信息的SQL语句
printf("%s\n", sql); // 打印SQL语句
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) // 执行SQL语句
{
printf("%s\n", errmsg); // 执行失败提示
strcpy(msg->data, "usr name already exist."); // 设置错误信息
}
else
{
printf("client register ok!\n"); // 注册成功提示
strcpy(msg->data, "OK!"); // 设置成功信息
}
if(send(acceptfd, msg, sizeof(MSG), 0) < 0) // 发送响应给客户端
{
perror("fail to send"); // 发送失败提示
return;
}
return;
}
// 处理登录请求函数
int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
char sql[512] = {}; // SQL语句
char *errmsg; // 错误信息指针
int nrow; // 查询结果行数
int ncloumn; // 查询结果列数
char **resultp; // 查询结果
snprintf(sql, sizeof(sql), "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data); // 构造查询用户信息的SQL语句
printf("%s\n", sql); // 打印SQL语句
if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK) // 执行SQL查询
{
printf("%s\n", errmsg); // 执行失败提示
return -1; // 返回错误
}
else
{
printf("get_table ok!\n"); // 查询成功提示
}
// 查询成功,数据库中拥有此用户
if(nrow == 1)
{
strcpy(msg->data, "OK"); // 设置成功信息
send(acceptfd, msg, sizeof(MSG), 0); // 发送响应给客户端
return 1; // 返回成功
}
if(nrow == 0) // 密码或者用户名错误
{
strcpy(msg->data, "usr/passwd wrong."); // 设置错误信息
send(acceptfd, msg, sizeof(MSG), 0); // 发送响应给客户端
}
return 0; // 返回失败
}
// 处理查询单词请求函数
int do_searchword(int acceptfd, MSG *msg, char word[])
{
FILE * fp; // 文件指针
int len = 0; // 单词长度
char temp[512] = {}; // 临时缓冲区
int result; // 比较结果
char *p; // 指向注释部分的指针
// 打开文件,读取文件,进行比对
if((fp = fopen("dict.txt", "r")) == NULL)
{
perror("fail to fopen.\n"); // 打开文件失败提示
strcpy(msg->data, "Failed to open dict.txt"); // 设置错误信息
send(acceptfd, msg, sizeof(MSG), 0); // 发送响应给客户端
return -1; // 返回错误
}
// 打印出,客户端要查询的单词
len = strlen(word); // 获取单词长度
printf("%s, len = %d\n", word, len); // 打印单词及其长度
// 读文件,来查询单词
while(fgets(temp, 512, fp) != NULL)
{
// printf("temp:%s\n", temp);
// 比较文件中的单词与要查询的单词
result = strncmp(temp, word, len);
if(result < 0)
{
continue; // 继续读取下一行
}
if(result > 0 || ((result == 0) && (temp[len] != ' ')))
{
break; // 未找到单词,退出循环
}
// 表示找到了,查询的单词
p = temp + len; // 指向注释部分
// printf("found word:%s\n", p);
while(*p == ' ')
{
p++; // 跳过空格
}
// 找到了注释,跳跃过所有的空格
strcpy(msg->data, p); // 将注释复制到msg结构体中
printf("found word:%s\n", msg->data);
// 注释拷贝完毕之后,关闭文件
fclose(fp);
return 1; // 返回成功
}
fclose(fp); // 关闭文件
return 0; // 返回未找到
}
// 获取系统时间函数
int get_date(char *date)
{
time_t t; // 时间变量
struct tm *tp; // 时间结构体指针
time(&t); // 获取当前时间
// 进行时间格式转换
tp = localtime(&t);
sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec); // 格式化时间
printf("get date:%s\n", date); // 打印时间
return 0; // 返回成功
}
// 处理查询请求函数
int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
char word[64]; // 单词
int found = 0; // 查找到的标志
char date[128] = {}; // 日期
char sql[128] = {}; // SQL语句
char *errmsg; // 错误信息指针
// 拿出msg结构体中,要查询的单词
strcpy(word, msg->data);
found = do_searchword(acceptfd, msg, word); // 查询单词
printf("查询一个单词完毕.\n");
// 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。
if(found == 1)
{
// 需要获取系统时间
get_date(date);
sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word); // 构造插入历史记录的SQL语句
if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) // 执行SQL语句
{
printf("%s\n", errmsg); // 执行失败提示
return -1; // 返回错误
}
else
{
printf("Insert record done.\n"); // 插入成功提示
}
}
else // 表示没有找到
{
strcpy(msg->data, "Not found!"); // 设置未找到信息
}
// 将查询的结果,发送给客户端
send(acceptfd, msg, sizeof(MSG), 0);
return 0; // 返回成功
}
// 历史记录回调函数
int history_callback(void* arg, int f_num, char** f_value, char** f_name)
{
// record, name, date, word
int acceptfd;
MSG msg;
acceptfd = *((int *)arg); // 获取套接字描述符
sprintf(msg.data, "%s , %s", f_value[1], f_value[2]); // 格式化历史记录
send(acceptfd, &msg, sizeof(MSG), 0); // 发送历史记录给客户端
return 0; // 返回成功
}
// 处理历史记录请求函数
int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
char sql[128] = {}; // SQL语句
char *errmsg; // 错误信息指针
sprintf(sql, "select * from record where name = '%s'", msg->name); // 构造查询历史记录的SQL语句
// 查询数据库
if(sqlite3_exec(db, sql, history_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK)
{
printf("%s\n", errmsg); // 查询失败提示
}
else
{
printf("Query record done.\n"); // 查询成功提示
}
// 所有的记录查询发送完毕之后,给客户端发出一个结束信息
msg->data[0] = '\0';
send(acceptfd, msg, sizeof(MSG), 0); // 发送结束信息给客户端
return 0; // 返回成功
}
建立数据库
运行效果
三,QT回顾
udp客户端:
#include "widget.h"
#include <QVBoxLayout>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
te = new QTextEdit;
le = new QLineEdit;
pb = new QPushButton("send");
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(te);
vbox->addWidget(le);
vbox->addWidget(pb);
setLayout(vbox);
udpsock = new QUdpSocket;
connect(pb, SIGNAL(clicked(bool)), this, SLOT(senddata()));
connect(udpsock, SIGNAL(readyRead()), this, SLOT(recvdata()));
}
Widget::~Widget()
{
}
udp服务端
#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <QObject>
#include <QUdpSocket>
#include <QDebug>
class udpServer : public QObject
{
Q_OBJECT
public:
explicit udpServer(QObject *parent = 0);
void init()
{
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::AnyIPv4, 8888);
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(readPendingDatagrams()));
qDebug()<<"init....";
}
signals:
public slots:
void readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
//processTheDatagram(datagram);
qDebug()<<"recv: "<<datagram;
udpSocket->writeDatagram(datagram.data(), datagram.size(),
sender, senderPort);
}
}
private:
QUdpSocket *udpSocket;
};
#endif // UDPSERVER_H
运行效果