Part 1.电子词典代码
一.功能
客户端可以注册登录退出(一级导航栏),查词查找自己查询的记录和返回上一级导航栏(二级导航栏)
服务器端需创建用户数据库(信息表和记录表)和单词数据库,并完成与客户端的反馈和完成其功能。
二.服务器端
cs
#include<myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.109.31"
//创建单词数据库和数据表
sqlite3 * create_cindb()
{
sqlite3 * dicp = NULL;
if(sqlite3_open("./dic.db",&dicp) != SQLITE_OK)
{
printf("dic.db sqlite3_open error, errcode = %d,errmsg = %s\n",sqlite3_errcode(dicp),sqlite3_errmsg(dicp));
return NULL;
}
printf("单词数据库创建完成\n");
char *sql = "create table if not exists DICTABLE(dic char,dir char);";
char *errmsg = NULL;
if(sqlite3_exec(dicp,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
printf("dic.db.DICTABLE create table error:%s\n",errmsg);
sqlite3_free(errmsg);
return NULL;
}
return dicp;
}
//创建用户数据库和数据表
sqlite3 * create_usrdb()
{
sqlite3 * usrp = NULL;
if(sqlite3_open("./usr.db",&usrp) != SQLITE_OK)
{
printf("usr.db sqlite3_open error, errcode = %d,errmsg = %s\n",sqlite3_errcode(usrp),sqlite3_errmsg(usrp));
return NULL;
}
printf("用户数据表创建成功\n");
//用户信息表
char *msgsql = "create table if not exists USRMSG(usrname char,usrpwd char,usrtext int);";
char *msgerrmsg = NULL;
if(sqlite3_exec(usrp,msgsql,NULL,NULL,&msgerrmsg) != SQLITE_OK)
{
printf("usr.db.USRMSG create table error\n");
sqlite3_free(msgerrmsg);
return NULL;
}
//用户记录表
char *mrysql = "create table if not exists USRMRY(usrname char,memory char,time char);";
char *mryerrmsg = NULL;
if(sqlite3_exec(usrp,mrysql,NULL,NULL,&mryerrmsg) != SQLITE_OK)
{
printf("usr.db.USRMSG create table error\n");
sqlite3_free(mryerrmsg);
return NULL;
}
return usrp;
}
//读取文件录入单词和注释
int dicin(FILE * fp,sqlite3 * dicp)
{
char str[128] = "";
char dic[128] = "";
char dir[128] = "";
while(fgets(str, sizeof(str), fp) != NULL)
{
// 去除换行符
str[strcspn(str, "\n")] = '\0';
// 重置变量
dic[0] = '\0';
dir[0] = '\0';
int word_len = 0;
// 找到第一个空格的位置
for(int i = 0; i < strlen(str); i++)
{
if(str[i] == ' ')
{
word_len = i;
break;
}
}
// 将单词复制到dic内
if(word_len > 0)
{
strncpy(dic, str, word_len);
dic[word_len] = '\0';
}
else
strcpy(dic, str);
//将注释复制到dir内
if(word_len > 0 && word_len < strlen(str))
{
int comment_start = word_len + 1;
strcpy(dir, str + comment_start);
}
char sql[128] = "";
sprintf(sql,"insert into DICTABLE values(\"%s\",\"%s\");",dic,dir);
char * errmsg = NULL;
if(sqlite3_exec(dicp,sql,NULL,NULL,&errmsg) != SQLITE_OK)
{
printf("insert error :%s\n",errmsg);
sqlite3_free(errmsg);
return -1;
}
}
printf("单词录入完成\n");
return 0;
}
//登录函数
int login(sqlite3 * usrp, int num, char *name, char *pwd, int new_fd)
{
char createsql[128] = "";
char * createerrmsg = NULL;
char loginsql[128] = "";
char * loginerrmsg = NULL;
char **resPtr = NULL;
int rows = 0;
int clos = 0;
char response[2] = "";
switch(num)
{
case 1: // 注册
sprintf(createsql,"insert into USRMSG values(\"%s\",\"%s\",%d);",name,pwd,0);
if(sqlite3_exec(usrp,createsql,NULL,NULL,&createerrmsg) != SQLITE_OK)
{
printf("create error :%s\n",createerrmsg);
sqlite3_free(createerrmsg);
strcpy(response, "0"); // 注册失败
send(new_fd, response, strlen(response), 0);
return -1;
}
strcpy(response, "1"); // 注册成功
send(new_fd, response, strlen(response), 0);
break;
case 2: // 登录
sprintf(loginsql,"select * from USRMSG where usrname = \"%s\";",name);
if(sqlite3_get_table(usrp,loginsql,&resPtr,&rows,&clos,&loginerrmsg) != SQLITE_OK)
{
printf("login select error:%s\n",loginerrmsg);
sqlite3_free(loginerrmsg);
strcpy(response, "0"); // 登录失败
send(new_fd, response, strlen(response), 0);
return -1;
}
// 检查是否找到用户
if(rows == 0)
{
strcpy(response, "0"); // 用户不存在
send(new_fd, response, strlen(response), 0);
sqlite3_free_table(resPtr);
return 0;
}
char *db_pwd = resPtr[clos + 1]; // 密码列
char *db_status = resPtr[clos + 2]; // 状态列
if(strcmp(db_status, "0") == 0) // 检查是否为已登录状态
{
if(strcmp(db_pwd, pwd) == 0) // 检查密码是否正确
{
char updsql[128] = "";
sprintf(updsql,"update USRMSG set usrtext = 1 where usrname = \"%s\";",name);
char * upderrmsg = NULL;
if(sqlite3_exec(usrp,updsql,NULL,NULL,&upderrmsg) != SQLITE_OK)
{
printf("update error :%s\n",upderrmsg);
sqlite3_free(upderrmsg);
strcpy(response, "0"); // 更新状态失败
send(new_fd, response, strlen(response), 0);
sqlite3_free_table(resPtr);
return -1;
}
strcpy(response, "1"); // 登录成功
send(new_fd, response, strlen(response), 0);
sqlite3_free_table(resPtr);
return 1;
}
else
{
strcpy(response, "0"); // 密码错误
send(new_fd, response, strlen(response), 0);
sqlite3_free_table(resPtr);
return 0;
}
}
else if(strcmp(db_status, "1") == 0)
{
strcpy(response, "2"); // 已有账号在线
send(new_fd, response, strlen(response), 0);
sqlite3_free_table(resPtr);
return 2;
}
sqlite3_free_table(resPtr);
return 0;
}
return 0;
}
//时间函数
char* mymktime()
{
time_t a = time(NULL);
struct tm *p = localtime(&a);
static char str[128] = "";
sprintf(str,"%d.%d.%d %02d:%02d:%02d",p->tm_year+1900,p->tm_mon+1,p->tm_mday,p->tm_hour,p->tm_min,p->tm_sec);
return str;
}
//查询单词和查询数据函数
int dic_select(int num,sqlite3 * dicp,sqlite3 * usrp,char * str,char * name,int new_fd)
{
char dicslg[128] = "";
char imeyslg[128] = "";
char smeyslg[128] = "";
char *errmsg = NULL;
char *mrymsg = NULL;
char **resPtr = NULL;
int rows = 0;
int clos = 0;
char rbuf[128] = "\0";
switch(num)
{
case 1:
sprintf(dicslg,"select * from DICTABLE where dic = \"%s\";",str);
if(sqlite3_get_table(dicp,dicslg,&resPtr,&rows,&clos,&errmsg) != SQLITE_OK)//查询单词注释
{
printf("dic select error:%s\n",errmsg);
sqlite3_free(errmsg);
return -1;
}
if(rows > 0)
{
char *time = mymktime();//时间函数
sprintf(imeyslg,"insert into USRMRY values(\"%s\",\"%s\",\"%s\");",name,str,time);//记录插入到数据库(usrp.USRMRY),格式:用户名 单词 时间
if(sqlite3_exec(usrp,imeyslg,NULL,NULL,&mrymsg) != SQLITE_OK)
{
printf("memory insert error :%s\n",mrymsg);
sqlite3_free(mrymsg);
}
sprintf(rbuf,"%s\t%s",resPtr[clos], resPtr[clos+1]); // 使用动态索引
send(new_fd,rbuf,strlen(rbuf),0);
}
else
{
sprintf(rbuf, "未找到单词: %s", str);
send(new_fd, rbuf, strlen(rbuf), 0);
}
sqlite3_free_table(resPtr);
break;
case 2:
sprintf(smeyslg,"select * from USRMRY where usrname = \"%s\";",name);//查询数据
if(sqlite3_get_table(usrp,smeyslg,&resPtr,&rows,&clos,&errmsg) != SQLITE_OK)
{
printf("memory select error:%s\n",errmsg);
sqlite3_free(errmsg);
return -1;
}
for(int i = 1; i <= rows; i++) // 从1开始,跳过列名
{
memset(rbuf,0,sizeof(rbuf));
for(int j = 0; j < clos; j++)
{
strcat(rbuf,resPtr[i*clos+j]);
if(j < clos -1)
strcat(rbuf,"\t");
}
strcat(rbuf,"\n");
send(new_fd,rbuf,strlen(rbuf),0);
}
sqlite3_free_table(resPtr);
break;
}
return 0;
}
// 更新用户登录状态为离线
void update_user_offline(sqlite3 *usrp, char *username)
{
if(username[0] != '\0')
{
char updsql[128] = "";
sprintf(updsql,"update USRMSG set usrtext = 0 where usrname = \"%s\";",username);
char * upderrmsg = NULL;
if(sqlite3_exec(usrp,updsql,NULL,NULL,&upderrmsg) != SQLITE_OK)
{
printf("update offline error :%s\n",upderrmsg);
sqlite3_free(upderrmsg);
}
else
printf("用户 %s 已离线\n", username);
}
}
int main(int argc, const char *argv[])
{
// 删除旧的数据库文件
if(access("./dic.db", F_OK) == 0)
{
system("rm dic.db");
printf("单词数据库删除完成\n");
}
if(access("./usr.db", F_OK) == 0)
{
system("rm usr.db");
printf("用户数据库删除完成\n");
}
//创建单词数据库和数据表
sqlite3 * dicp = create_cindb();
if(dicp == NULL)
{
printf("创建单词数据库失败\n");
return -1;
}
//创建用户数据库和数据表
sqlite3 * usrp = create_usrdb();
if(usrp == NULL)
{
printf("创建用户数据库失败\n");
sqlite3_close(dicp);
return -1;
}
FILE* fp = fopen("./dict.txt", "r");
if (fp == NULL)
{
ERR_MSG("open error");
sqlite3_close(dicp);
sqlite3_close(usrp);
return -1;
}
dicin(fp,dicp);
fclose(fp);
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sfd)
{
ERR_MSG("socket error");
sqlite3_close(dicp);
sqlite3_close(usrp);
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
if(-1 == bind(sfd,(struct sockaddr *)&sin,sizeof(sin)))
{
ERR_MSG("bind error");
close(sfd);
sqlite3_close(dicp);
sqlite3_close(usrp);
return -1;
}
if(-1 == listen(sfd,128))
{
ERR_MSG("listen error");
close(sfd);
sqlite3_close(dicp);
sqlite3_close(usrp);
return -1;
}
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1)
{
int new_fd = accept(sfd,(struct sockaddr *)&cin,&addrlen);
if(-1 == new_fd)
{
ERR_MSG("accept error");
continue;
}
pid_t pid = fork();
if(pid > 0)
close(new_fd);
else if(pid == 0)
{
close(sfd);
int flag = 0;
char logname[128] = ""; // 存储登录的用户名
while(1)//用户可以注册或者登录或者退出
{
char rbuf[128] = "";
int res = recv(new_fd,rbuf,sizeof(rbuf)-1,0);
if(res <= 0)
{
if(res == 0)
printf("客户端断开连接\n");
else
{
perror("recv error");
}
// 更新用户状态为离线
update_user_offline(usrp, logname);
close(new_fd);
exit(0);
}
rbuf[res] = '\0'; // 确保字符串结束
if(rbuf[0] == '1')//注册
{
int createnum = 1;
char crename[128] = "";
char crepwd[128] = "";
if(sscanf(rbuf + 2,"%s %s",crename,crepwd) == 2)
login(usrp,createnum,crename,crepwd,new_fd);
}
else if(rbuf[0] == '2')//登录
{
int loginnum = 2;
char logpwd[128] = "";
if(sscanf(rbuf + 2,"%s %s",logname,logpwd) == 2)
{
flag = login(usrp,loginnum,logname,logpwd,new_fd);
if(flag == 1)
printf("%s登录成功\n",logname);
}
if(flag == 1)//如果成功登录,则可以进行查词或者查记录或者退出
{
while(1)
{
char newrbuf[128] = "";
int res = recv(new_fd,newrbuf,sizeof(newrbuf)-1,0);
if(res <= 0)
{
if(res == 0)
printf("客户端断开连接\n");
else
perror("recv error");
// 更新用户状态为离线
update_user_offline(usrp, logname);
close(new_fd);
exit(0);
}
newrbuf[res] = '\0'; // 确保字符串结束
if(newrbuf[0] == '1') // 查单词
{
int dicnum2 = 1;
char dicstr[128] = "";
strcpy(dicstr,newrbuf+2);
dic_select(dicnum2,dicp,usrp,dicstr,logname,new_fd);
}
else if(newrbuf[0] == '2') // 查记录
{
int mrynum2 = 2;
char mrystr[128] = "";
strcpy(mrystr,newrbuf+2);
dic_select(mrynum2,dicp,usrp,mrystr,logname,new_fd);
}
else if(newrbuf[0] == '3') // 返回主菜单
{
// 更新用户状态为离线
update_user_offline(usrp, logname);
flag = 0;
break;
}
}
}
}
}
}
else
ERR_MSG("fork error");
}
sqlite3_close(dicp);
sqlite3_close(usrp);
close(sfd);
return 0;
}
三.客户端
cs
#define SER_PORT 8888
#define SER_IP "192.168.109.31"
#include<myhead.h>
// 连接服务器
int connect_server()
{
int sfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sfd)
{
ERR_MSG("socket error");
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
if (-1 == connect(sfd, (struct sockaddr*)&sin, sizeof(sin)))
ERR_MSG("connect error");
return sfd;
}
// 主菜单
int main_menu() {
printf("\n========== 电子词典 ==========\n");
printf("1. 注册\n");
printf("2. 登录\n");
printf("3. 退出\n");
printf("请选择操作: ");
int choice;
scanf("%d", &choice);
while (getchar() != '\n');
return choice;
}
// 二级菜单
int second_menu()
{
printf("\n========== 功能菜单 ==========\n");
printf("1. 查单词\n");
printf("2. 查记录\n");
printf("3. 返回主菜单\n");
printf("请选择操作: ");
int choice;
scanf("%d", &choice);
while (getchar() != '\n');
return choice;
}
// 注册功能
int register_user(int sfd)
{
char username[128] = "";
char password[128] = "";
char send_buf[256] = "";
printf("\n========== 用户注册 ==========\n");
printf("请输入用户名: ");
scanf("%s", username);
printf("请输入密码: ");
scanf("%s", password);
snprintf(send_buf, sizeof(send_buf), "1 %s %s", username, password);
if (send(sfd, send_buf, strlen(send_buf), 0) < 0)
ERR_MSG("send error");
printf("注册请求已发送,等待服务器响应...\n");
}
// 登录功能
int login_user(int sfd, char *username)
{
char password[128] = "";
char send_buf[256] = "";
char recv_buf[128] = "";
printf("\n========== 用户登录 ==========\n");
printf("请输入用户名: ");
scanf("%s", username);
printf("请输入密码: ");
scanf("%s", password);
// 格式化发送数据: "2 用户名 密码"
snprintf(send_buf, sizeof(send_buf), "2 %s %s", username, password);
// 发送登录请求
if (send(sfd, send_buf, strlen(send_buf), 0) < 0)
ERR_MSG("send error");
// 接收服务器响应
int res = recv(sfd, recv_buf, sizeof(recv_buf) - 1, 0);
if (res < 0)
ERR_MSG("recv error");
recv_buf[res] = '\0';
// 解析服务器响应
if (strcmp(recv_buf, "1") == 0)
{
printf("登录成功!\n");
return 1;
} else if (strcmp(recv_buf, "2") == 0)
{
printf("该用户已在线!\n");
return 0;
} else
{
printf("用户名或密码错误!\n");
return 0;
}
}
// 查询单词功能
int query_word(int sfd, const char *username)
{
char word[128] = "";
char send_buf[256] = "";
char recv_buf[512] = "";
printf("\n========== 查询单词 ==========\n");
printf("请输入要查询的单词: ");
scanf("%s", word);
snprintf(send_buf, sizeof(send_buf), "1 %s", word);
// 发送查询请求
if (send(sfd, send_buf, strlen(send_buf), 0) < 0)
ERR_MSG("send error");
int res = recv(sfd, recv_buf, sizeof(recv_buf) - 1, 0);
if (res < 0)
ERR_MSG("recv error");
recv_buf[res] = '\0';
printf("查询结果: %s\n", recv_buf);
}
// 查询记录功能
int query_history(int sfd, const char *username)
{
char send_buf[256] = "";
char recv_buf[1024] = "";
printf("\n========== 查询记录 ==========\n");
// 格式化发送数据: "2 用户名"
snprintf(send_buf, sizeof(send_buf), "2 %s", username);
// 发送查询请求
if (send(sfd, send_buf, strlen(send_buf), 0) < 0)
ERR_MSG("send error");
// 接收服务器响应
printf("查询记录:\n");
while (1) {
int res = recv(sfd, recv_buf, sizeof(recv_buf) - 1, 0);
if (res < 0)
ERR_MSG("recv error");
if (res == 0)
break;
recv_buf[res] = '\0';
if (strstr(recv_buf, "END") != NULL)
{
char *end_pos = strstr(recv_buf, "END");
*end_pos = '\0';
printf("%s", recv_buf);
break;
}
printf("%s", recv_buf);
}
}
int main()
{
int sfd;
int logged_in = 0;
char username[128] = "";
while (1) {
if (!logged_in)
{
int choice = main_menu();
sfd = connect_server();
switch (choice)
{
case 1:
register_user(sfd);
close(sfd);
break;
case 2:
if (login_user(sfd, username))
{
logged_in = 1;
} else
{
close(sfd);
}
break;
case 3:
exit(0);
default:
printf("无效选择,请重新输入!\n");
close(sfd);
break;
}
}
else
{
int choice = second_menu();
switch (choice)
{
case 1:
query_word(sfd, username);
break;
case 2:
query_history(sfd, username);
break;
case 3:
printf("返回主菜单...\n");
close(sfd);
logged_in = 0;
break;
default:
printf("无效选择,请重新输入!\n");
break;
}
}
}
return 0;
}
Part 2.sqlite3_get_table函数
一.功能
功能:通过执行sql语句,得到结果集中的内容
二.函数原型
int sqlite3_get_table( sqlite3 *db,
const char *zSql,
char ***pazResult,
int *pnRow,
int *pnColumn,
char **pzErrmsg
);
三.参数
sqlite3 *db:数据库句柄
const char *zSql:要执行的sql语句
char ***pazResult:查询结果的起始地址,需要定义一个二级指针变量,将地址进行传递
int *pnRow: 查询结果的行数(不包括表头)
int *pnColumn:查询结果的列数
char **pzErrmsg :错误信息