前言:
我们将前面学习的知识总结练习一个项目,也是便于回顾,涉及的技术有sqlite3数据库,TCP通信
dict_server.c
#include <stdio.h>
#include<sqlite3.h>
#include<string.h>
#include<stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<sys/stat.h>//基于TCP通信,使用数据库完成电子词典功能
//服务端
int socket_ok = -1;
char sql[512];//存储服务端发来的sql
char name[12]; //存储当前登录人的名字//登录管理
void login(sqlite3* mydb){char** resultp; //resultp里面存储了数据表中的所有数据int row; int column;//接收账号char id[12];memset(id,0,sizeof(id));recv(socket_ok,id,sizeof(id),0);//接收账号char password[12];memset(password,0,sizeof(password));recv(socket_ok,password,sizeof(password),0);//接收名字memset(name,0,sizeof(name));recv(socket_ok,name,sizeof(name),0);snprintf(sql,sizeof(sql),"select * from user where id = '%s' and password = '%s' and name = '%s'",id,password,name);//执行sqlsqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);char resultStr[4096] = "";//存储结果集memset(resultStr,0,sizeof(resultStr));//拼接字符串for(int i=0;i<(row+1)*column;i++)//(row+1)这里加1,是因为没有把头算进去,我们要想输出头,就需要+1{strcat(resultStr, resultp[i]);//拼接字符串strcat(resultStr, "\t\t"); // 使用制表符分隔if((i+1)%column == 0)//这里是换行,每当下标i取模列为0的时候换行{strcat(resultStr, "\n");//换行}}//发送数据send(socket_ok,resultStr,sizeof(resultStr),0);memset(sql,0,sizeof(sql));}//注册管理
void user_register(sqlite3* mydb){char** resultp;int row; int column;//接收账号char id[12];memset(id,0,sizeof(id));recv(socket_ok,id,sizeof(id),0);//接收账号char password[12];memset(password,0,sizeof(password));recv(socket_ok,password,sizeof(password),0);//接收名字char name[12];memset(name,0,sizeof(name));recv(socket_ok,name,sizeof(name),0);//首先检查账号合不合法char sql2[128] = "select * from user;";sqlite3_get_table(mydb,sql2,&resultp,&row,&column,NULL);for(int i=0;i<(row+1)*column;i++){if(strcmp(id,resultp[i])==0){//账号重复,非法char msg[12] = "-1";send(socket_ok,msg,strlen(msg),0);return;}}//到这里账号合法snprintf(sql,sizeof(sql),"insert into user(id,password,name)values('%s','%s','%s')",id,password,name);//执行sqlsqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);char msg2[12] = "1";send(socket_ok,msg2,strlen(msg2),0);memset(sql,0,sizeof(sql));}//记录查询单词记录
void history(sqlite3* mydb,char str1[],char str2[]){char** resultp;int row; int column;//拼接sqlmemset(sql,0,sizeof(sql));snprintf(sql,sizeof(sql),"insert into history(name,word,explain)values('%s','%s','%s');",name,str1,str2);printf("%s\n",sql);sqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);memset(sql,0,sizeof(sql));
}//字典功能
void dict(sqlite3* mydb){//首先读出字典的数据//使用标准io打开字典文件FILE* fp = fopen("./dict.txt","r");if(fp==NULL){perror("open dict.txt failed");return;}//接收查询的单词char msg[30];memset(msg,0,sizeof(msg));recv(socket_ok,msg,sizeof(msg),0);printf("查询%s\n",msg);char buf[1024];//存储一行数据memset(buf,0,sizeof(buf));char msg2[1024];//存储单词解释//feof 函数用于检查文件流的文件结束标志。如果 feof(fp) 返回 0(假),//表示文件流 fp 还没有到达文件的末尾,如果返回非零值(真),表示文件流 fp 已经到达文件的末尾。while(fgets(buf,sizeof(buf),fp)!=NULL && !feof(fp)){//确保读到了文件尾//这里为了避免记录单词查询记录时出错,我们把buf最后的回车符去掉size_t ret = strcspn(buf,"\n");// 使用strcspn函数找到回车符的位置// 如果找到回车符,则将其替换为字符串结束符'\0'if (buf[ret] == '\n') {buf[ret] = '\0';}char dest[25];//存储读出来的单词memset(dest,0,sizeof(dest));//清0//sscanf 函数在提取数据时是以空白字符(例如空格、制表符、换行符等)作为分隔符的。当你使用 "%s" 格式指定来提取一个字符串时//,sscanf 会从输入字符串 input 中找到第一个非空白字符,然后开始提取字符,直到再次遇到空白字符为止。sscanf(buf,"%s",dest);//观察字典文件可知,只需要拷贝前17个字节中的单词部分if( strcmp(msg,dest) == 0 ){//匹配查询的单词memset(msg2,0,sizeof(msg2));strcpy(msg2,&buf[17]);//从第18个字节开始一直到最后,就是单词的解释send(socket_ok,msg2,strlen(msg2),0);history(mydb,msg,msg2);//查询到记录查询return;}memset(buf,0,sizeof(buf));}//程序到这里说明没有找到单词char msg3[64] = "-1";send(socket_ok,msg3,strlen(msg3),0);fclose(fp);
}//查看单词查询记录
void dict_history(sqlite3* mydb){//操作数据库,执行特定的sql语句snprintf(sql,sizeof(sql),"select * from history where name = '%s';",name);printf("%s\n",sql);//操作数据库char** resultp; //resultp里面存储了数据表中的所有数据int row; int column;sqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);char resultStr[4096] = "";//存储结果集memset(resultStr,0,sizeof(resultStr));//拼接字符串for(int i=0;i<(row+1)*column;i++)//(row+1)这里加1,是因为没有把头算进去,我们要想输出头,就需要+1{strcat(resultStr, resultp[i]);//拼接字符串strcat(resultStr, "\t\t"); // 使用制表符分隔if((i+1)%column == 0)//这里是换行,每当下标i取模列为0的时候换行{strcat(resultStr, "\n");//换行}}//发送数据send(socket_ok,resultStr,sizeof(resultStr),0);memset(sql,0,sizeof(sql));
}//收藏单词
void shoucang_word(sqlite3* mydb){//操作数据库char** resultp; int row; int column;//接收要收藏的单词char word[12];memset(word,0,sizeof(word));recv(socket_ok,word,sizeof(word),0);//操作数据库,执行特定的sql语句snprintf(sql,sizeof(sql),"insert into shoucang(name,word)values('%s','%s')",name,word);printf("%s\n",sql);sqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);//返回char msg[12]="1";send(socket_ok,msg,sizeof(msg),0);memset(sql,0,sizeof(sql));
}//查看收藏的单词
void sel_shoucnag(sqlite3* mydb){//操作数据库char** resultp; int row; int column;//操作数据库,执行特定的sql语句snprintf(sql,sizeof(sql),"select distinct shoucang.*,history.explain from shoucang,history where shoucang.word = history.word and shoucang.name='%s';",name);printf("%s\n",sql);sqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);char resultStr[4096] = "";//存储结果集memset(resultStr,0,sizeof(resultStr));//拼接字符串for(int i=0;i<(row+1)*column;i++)//(row+1)这里加1,是因为没有把头算进去,我们要想输出头,就需要+1{strcat(resultStr, resultp[i]);//拼接字符串strcat(resultStr, "\t\t"); // 使用制表符分隔if((i+1)%column == 0)//这里是换行,每当下标i取模列为0的时候换行{strcat(resultStr, "\n");//换行}}//发送数据send(socket_ok,resultStr,sizeof(resultStr),0);memset(sql,0,sizeof(sql));
}//删除收藏的单词
void del_shoucang(sqlite3* mydb){//操作数据库char** resultp; int row; int column;//先查询所有收藏的单词char sql2[128];//拼接字符串snprintf(sql2,sizeof(sql2),"select * from shoucang where name = '%s';",name);sqlite3_get_table(mydb,sql2,&resultp,&row,&column,NULL);char resultStr[4096] = "";//存储结果集memset(resultStr,0,sizeof(resultStr));//拼接字符串for(int i=0;i<(row+1)*column;i++)//(row+1)这里加1,是因为没有把头算进去,我们要想输出头,就需要+1{strcat(resultStr, resultp[i]);//拼接字符串strcat(resultStr, "\t\t"); // 使用制表符分隔if((i+1)%column == 0)//这里是换行,每当下标i取模列为0的时候换行{strcat(resultStr, "\n");//换行}}//发送数据send(socket_ok,resultStr,sizeof(resultStr),0);//接收要删除的单词char del_word[12];memset(del_word,0,sizeof(del_word));recv(socket_ok,del_word,sizeof(del_word),0);//拼接字符串snprintf(sql,sizeof(sql),"delete from shoucang where word = '%s'and name = '%s';",del_word,name);printf("%s\n",sql);sqlite3_get_table(mydb,sql,&resultp,&row,&column,NULL);//返回char msg[12]="1";send(socket_ok,msg,sizeof(msg),0);memset(sql,0,sizeof(sql));}int main(int argc,char** argv)
{//打开数据库sqlite3* mydb = NULL; int ret = sqlite3_open("./dict.db",&mydb);if(ret==-1){perror("open failed");return -1;}//1. 创建套接字int socket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd == -1){printf("创建套接字失败\n");return -1;}//2. 绑定套接字与服务端网络地址struct sockaddr_in s_addr;s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));s_addr.sin_addr.s_addr = inet_addr(argv[1]);//绑定int bind_ok = bind(socket_fd,(struct sockaddr*)&s_addr,sizeof(s_addr));if(bind_ok==-1){perror("bind failed");}//3. 设置监听int socket_listen = listen(socket_fd,4);//4.等待对端连接printf("等待连接\n");struct sockaddr_in c_addr;//保存客户端地址信息socklen_t size_len = sizeof(c_addr);socket_ok = accept(socket_fd,(struct sockaddr*)&c_addr,&size_len);if(socket_ok==-1){perror("连接失败\n");return -1;}printf("连接成功\n");char buf[128];while(1){//5.接收客户端发来的sql语句memset(buf,0,sizeof(buf));recv(socket_ok,buf,sizeof(buf),0);//阻塞等待,接收操作标志位printf("服务端接收到 %s 号操作\n",buf);int input = atoi(buf);switch (input){case 1:login(mydb);break;case 2:user_register(mydb);break;case 3:dict(mydb);break;case 4:dict_history(mydb);break;case 5:shoucang_word(mydb);break;case 6:sel_shoucnag(mydb);break;case 7:del_shoucang(mydb);break;case 0:exit(0);break;default:printf("输入错误,请重新输入\n");break;}}close(socket_fd);close(socket_ok);return 0;
}
dict_client.c
#include <stdio.h>
#include<sqlite3.h>
#include<string.h>
#include<stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include<sys/types.h>
#include<sys/stat.h>//基于TCP通信,使用数据库完成电子词典功能
//客户端int socket_fd = -1;
char result[4096];//登录管理
int login(){//发送操作标志位char buf[12] = "1";send(socket_fd,buf,sizeof(buf),0);char id[12];printf("请输入4位账号:");scanf("%s",id);//发送账号send(socket_fd,id,strlen(id),0);char password[12];printf("请输入3位密码:");scanf("%s",password);//发送密码send(socket_fd,password,strlen(password),0);char name[12];printf("请输入你的姓名:");scanf("%s",name);send(socket_fd,name,strlen(name),0);//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(result[0] != '\0'){//有账号且密码正确return 1;}else{//无账号或账号密码错误return -1;}}//注册管理
void user_register(){//发送操作标志位char buf[12] = "2";send(socket_fd,buf,sizeof(buf),0);char id[12];printf("请输入4位账号:");scanf("%s",id);send(socket_fd,id,strlen(id),0);char password[12];printf("请输入3位密码:");scanf("%s",password);send(socket_fd,password,strlen(password),0);char name[12];printf("请输入你的姓名:");scanf("%s",name);send(socket_fd,name,strlen(name),0);//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(strcmp(result,"1")==0){//注册成功printf("账号注册成功\n");}else if(strcmp(result,"-1")==0){//账号非法printf("账号重复,请重新输入\n");}
}//退出
void quit(){//发送操作标志位char buf[12] = "0";send(socket_fd,buf,sizeof(buf),0);}//收藏单词
void shoucang_word(char word[]){//发送操作标志位char buf[12] = "5";send(socket_fd,buf,sizeof(buf),0);//发送给收藏的单词给服务端send(socket_fd,word,strlen(word),0);//接收结果memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);if(strcmp(result,"1")==0){printf("收藏成功\n");}else{printf("收藏失败\n");}}//字典功能
void dict(){//发送操作标志位char buf[12] = "3";send(socket_fd,buf,sizeof(buf),0);char search[30];printf("请输入你要查询的单词:");scanf("%s",search);//发送给服务端查询send(socket_fd,search,strlen(search),0);//接收查询结果//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);if(strcmp(result,"-1")==0){printf("抱歉你的输入有误 或者 本词典没有收录此单词\n");}else{//查询成功printf("%s\n",result);printf("\n");//只有查询成功,才有收藏的机会char input[12];memset(input,0,sizeof(input));printf("是否收藏此单词(yes/no)?");scanf("%s",input);if(strcmp(input,"yes")==0){//调用收藏函数shoucang_word(search);}}}//查看单词查询记录
void dict_history(){//发送操作标志位char buf[128] = "4";send(socket_fd,buf,sizeof(buf),0);//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(result[0] != '\0'){printf("%s\n",result);}else{printf("查询失败\n");}
}//查看收藏的单词
void sel_shoucnag(){//发送操作标志位char buf[128] = "6";send(socket_fd,buf,sizeof(buf),0);//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(result[0] != '\0'){printf("%s\n",result);}else{printf("查询失败\n");}}//删除收藏的单词
void del_shoucang(){//发送操作标志位char buf[128] = "7";send(socket_fd,buf,sizeof(buf),0);//接收所有收藏的单词数据,供用户选择//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(result[0] != '\0'){printf("%s\n",result);char del_word[30];printf("请输入要删除的单词:");scanf("%s",del_word);send(socket_fd,del_word,strlen(del_word),0);//接收消息memset(result,0,sizeof(result));recv(socket_fd,result,sizeof(result),0);//阻塞等待if(result[0] != '\0'){printf("删除成功\n");}else{printf("删除失败\n");}}else{printf("查询失败\n");}//接收消息}void menu(){printf("*********************************************************\n");printf("**** 1. dict 2. dict_history ****\n");printf("**** 3. sel_shoucnag 4. del_shoucang ****\n");printf("**** 0. exit ****\n");printf("*********************************************************\n");
}void menu2(){printf("*********************************************************\n");printf("**** 1.login 2.register ****\n");printf("*********************************************************\n");
}void caidan(){int input;do{printf("\n");sleep(1);menu();printf("请输入你的操作:");scanf("%d",&input);switch (input){case 1:dict();break;case 2:dict_history();break;case 3:sel_shoucnag();break;case 4:del_shoucang();break;case 0:quit();break;default:printf("输入错误,请重新输入\n");break;}}while(input);
}int main(int argc,char** argv)
{//1. 创建套接字socket_fd = socket(AF_INET,SOCK_STREAM,0);if(socket_fd == -1){printf("创建套接字失败\n");return -1;}//2.初始化服务端网络地址struct sockaddr_in s_addr;s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));s_addr.sin_addr.s_addr = inet_addr(argv[1]);//3. 请求连接connect(socket_fd,(struct sockaddr*)&s_addr,sizeof(s_addr));//先登录int in;do{printf("\n");sleep(1);menu2();printf("请输入你的操作:");scanf("%d",&in);int ret = -1;switch (in){case 1:ret = login();if(ret==1){printf("登录成功\n");caidan();exit(0);}else if(ret == -1){printf("账号或者密码错误\n");}break;case 2:user_register();break;default:printf("输入错误,请重新选择\n");break;}}while(in);close(socket_fd);return 0;
}