目录
C语言——在线词典
一、需求分析
二、客户端功能设计
三、设计数据库mydict.db
四、引入词典文件-dict.txt
五、代码实现
1、 客户端:
2、 服务器端:
六、效果展示
功能介绍:基于Linux操作系统、网络编程和数据库实现在线词典,客户端可以进行注册、登录、查询、查看历史记录和退出功能,服务端基于TCP通信,多线程实现并发访问客户端,并利用sqlite3数据库实现对用户信息的管理。
1.注册:若用户名已经注册过,可重新注册
2.登录:用户名或密码错误需重新登录
3.查询:输入要查的单词,#键结束查询
4.历史:可以查询当前用户历史查找过的单词
5.退出:退出在线词典
这里数据库已经设计好了,创建了两个表,一个是user表,用来存放用户名和密码;一个是record表,用来存放查询过的的单词、查询时间和查询用户。
部分词典内容展示,这里截图显示。
//客户端
#include
#include
#include
#include
#include
#include
#include
#include
#define N 20
#define M 200
#define R 1 //注册
#define L 2 //登录
#define Q 3 //查询单词
#define H 4 //历史记录
#define E 5 //退出
typedef struct{
int type;//消息类型
char name[N];//用户名
char text[M];//单词 或 密码
}MSG;
#define LEN_SMG sizeof(MSG)
typedef struct sockaddr SA;
//用户注册
void do_register(int sockfd,MSG *pbuf){
pbuf->type = R;
printf("用户名:");
scanf("%s",pbuf->name);
printf("密码:");
scanf("%s",pbuf->text);
send(sockfd,pbuf,LEN_SMG,0);
recv(sockfd,pbuf,LEN_SMG,0);
printf("%s\n",pbuf->text);
sleep(1);
}
//用户登录
int do_login(int sockfd,MSG *pbuf){
pbuf->type = L;
printf("用户名:");
scanf("%s",pbuf->name);
printf("密码:");
scanf("%s",pbuf->text);
send(sockfd,pbuf,LEN_SMG,0);//发送给服务器
recv(sockfd,pbuf,LEN_SMG,0);//从服务器接收
if(pbuf->type == 8){
printf("%s\n",pbuf->text);
sleep(1);
return 1;
}
else{
printf("%s\n",pbuf->text);
return 0;
}
}
//查询单词
void do_query(int sockfd,MSG *pbuf){
pbuf->type = Q;
while(1)
{
printf("请输入你要查询的单词(输入#结束):");
scanf("%s",pbuf->text);
getchar();
//客户端输入#返回上一级菜单
if(strncmp(pbuf->text,"#",1) == 0)
break;
//将要查询的单词发送给服务器
send(sockfd,pbuf,LEN_SMG,0);
//等待接受服务器,传递回来的单词的注释信息
recv(sockfd,pbuf,LEN_SMG,0);
printf("就是这个意思:%s\n",pbuf->text);
}
}
//查询历史记录
int do_history(int sockfd,MSG *pbuf){
pbuf->type = H;
send(sockfd,pbuf,LEN_SMG,0);
//接受服务器,传递回来的历史信息
while(1){
recv(sockfd,pbuf,LEN_SMG,0);
//历史单词全部接受完成
if(pbuf->text[0] == '0')
break;
//输出历史信息
printf("%s\n",pbuf->text);
}
return 0;
}
//二级菜单
void menu_2(int sockfd,MSG *pbuf){
while(1){
printf("* 欢迎使用电子词典 *\n");
printf("****************************************\n");
printf("* 1.查询单词 2.查询历史单词 3.退出 *\n");
printf("****************************************\n");
printf("请选择:");
scanf("%d",&pbuf->type);
switch(pbuf->type){
case 1:
do_query(sockfd,pbuf);
break;
case 2:
do_history(sockfd,pbuf);
break;
case 3:
printf(" 即将退出,欢迎再次使用!\n");
send(sockfd,pbuf,LEN_SMG,0);
sleep(1);
close(sockfd);
exit(-1);
break;
default:
printf("错误选项!\n");
break;
}
}
}
//一级菜单
void menu_1(int sockfd,MSG *pbuf){
int num;//选择功能
while(1){
printf("* 电子词典项目 *\n");
printf("********************************\n");
printf("* 1.注册 2.登录 3.退出 *\n");
printf("********************************\n");
printf("请选择:");
scanf("%d",&num);
switch(num){
case 1:
do_register(sockfd,pbuf);
break;
case 2:
while(do_login(sockfd,pbuf) != 1)
continue;
menu_2(sockfd,pbuf);
break;
case 3:
printf("即将退出,欢迎下次使用!\n");
sleep(1);
close(sockfd);
exit(-1);
default:
printf("不存在此项操作!\n");
break;
}
}
}
int main(){
int sockfd;
//创建套接字
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0){
printf("fail to socket\n");
return -1;
}
socklen_t len = sizeof(SA);
MSG buf;
struct sockaddr_in serveraddr;//结构体
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(8888);//端口号
serveraddr.sin_addr.s_addr = inet_addr("192.168.2.128");//ip地址
//请求服务端建立连接
connect(sockfd,(SA *)&serveraddr,len);
//与服务器建立连接后,进入菜单界面
menu_1(sockfd,&buf);
return 0;
}
//服务端
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#define PATH_DICT "./dict.txt" //单词表
#define PATH_DATA "./mydict.db" //数据库
#define N 20
#define SIZE 256
#define R 1 //注册
#define L 2 //登录
#define Q 3 //查询单词
#define H 4 //历史记录
#define E 5 //退出
typedef struct{
int type;//消息类型
char name[N];//用户名
char data[SIZE];//单词 或 密码
}MSG;
#define LEN_SMG sizeof(MSG)
typedef struct sockaddr SA;
//注册功能 R
void do_register(int connfd,MSG *pbuf,sqlite3 *pdb){
char *errmsg,**result;
//nrow 查找出的总行数 ncolumn 存储列
int nrow = 0,ncolumn = 0;
char sql[1280];
sprintf(sql,"insert into user values('%s','%s');",pbuf->name,pbuf->data);
printf("%s\n",sql);
if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK){
printf("%s\n",errmsg);
strcpy(pbuf->data,"user name already exist");
}
else{
printf("client register ok!\n");
strcpy(pbuf->data,"OK!");
}
if(send(connfd,pbuf,LEN_SMG,0) < 0){
printf("fail to send\n");
return;
}
return;
}
//登录 L
void do_login(int connfd,MSG *pbuf,sqlite3 *pdb){
char *errmsg,**result;
char sql[1280]={};
int nrow = 0,ncloumn = 0;
sprintf(sql,"select * from user where name = '%s'and pass = '%s';",pbuf->name,pbuf->data);
printf("%s\n",sql);
if(sqlite3_get_table(pdb,sql,&result,&nrow,&ncloumn,&errmsg) != SQLITE_OK){
printf("%s\n",errmsg);
exit(-1);
}
//查询成功,数据库有此用户
if(nrow > 0)
{
sprintf(pbuf->data,"\n %s恭喜您,登录成功\n",pbuf->name);
pbuf->type = 8;
}
//密码或者用户名错误
else
{
sprintf(pbuf->data,"用户名或密码错误,请重新输入!\n");
}
send(connfd,pbuf,LEN_SMG,0);
}
int do_searchword(int connfd,MSG *pbuf,char word[])
{
char line[500];//用来保存读取的单词及解释
int ret;
char *p;
//打开文件,读取文件
FILE *fp = fopen(PATH_DICT,"r");
if(fp == NULL){
printf("fopen fail\n");
sprintf(pbuf->data,"抱歉,服务器打开词典失败\n");
send(connfd,pbuf,LEN_SMG,0);
return -1;
}
//打印出客户端要查询的单词
int len = strlen(word);
printf("%s,len = %d\n",word,len);
//读文件来查询单词
while(fgets(line,500,fp) != NULL)
{
ret = strncmp(line,word,len);
if(ret < 0)
{
continue;
}
if(ret == 0|| line[len] != ' ')
{
//表示找到查询的单词
p = line + len;
while(*p == ' ')
{
p++;
}
//找到注释,跳过所有空格
strcpy(pbuf->data,p);
printf("%s\n",pbuf->data);
send(connfd,pbuf,LEN_SMG,0);
//拷贝完毕后,关闭文件
fclose(fp);
return 1;
}
else
{
break;
}
}
strcpy(pbuf->data,"抱歉,没有找到该单词!\n");
send(connfd,pbuf,LEN_SMG,0);
fclose(fp);
return 0;
}
//获取系统时间
char get_date(char *date){
time_t t;
struct tm *tp = NULL;
time(&t);
//进行时间格式转换
tp = localtime(&t);
sprintf(date,"%02d-%02d-%02d %02d:%02d:%02d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec);
return 0;
}
//查询单词 Q
int do_query(int connfd,MSG *pbuf,sqlite3 *pdb){
char word[64];
int found = 0;
char date[128];//用来保存系统时间
char sql[1280];
char line[200];//用来保存读取的单词及解释
char *errmsg;
int ret;
//拿出pbuf结构体中要查询的单词
strcpy(word,pbuf->data);
found = do_searchword(connfd,pbuf,word);
//找到单词,此时应该将用户名时间和单词插入到历史记录表中去
if(found == 1)
{
//获取系统时间
get_date(date);
sprintf(sql,"insert into record values('%s','%s','%s');",pbuf->name,date,word);
printf("%s\n",sql);
if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK){
printf("%s\n",errmsg);
return -1;
}
}
//表示没有找到
else
{
sprintf(pbuf->data,"抱歉,没有找到该单词的意思\n");
}
//将查询结果发送给客户端
send(connfd,pbuf,LEN_SMG,0);
return 0;
}
//回调函数,每找到一条记录就执行一次该函数
int history_callback(void *arg,int f_column,char **f_value,char **f_name)
{
//f_column 记录包含的字段个数
//f_value 每个记录的值
//f_name 每个记录的字段名称
int connfd;
MSG pbuf;
connfd = *(int *)arg;
sprintf(pbuf.data,"%s,%s",f_value[1],f_value[2]);
send(connfd,&pbuf,sizeof(pbuf),0);
return 0;
}
//历史记录 H
void do_history(int connfd,MSG *pbuf,sqlite3 *pdb){
char sql[1280];
char *errmsg;
sprintf(sql,"select * from record where name = '%s'",pbuf->name);
//查询数据库
if(sqlite3_exec(pdb,sql,history_callback,(void *)&connfd,&errmsg) != SQLITE_OK){
printf("%s\n",errmsg);
exit(-1);
}
else
printf("请求成功\n");
//所有的记录查询完毕后,给客户端发送结束信息
pbuf->data[0] = '\0';
send(connfd,pbuf,LEN_SMG,0);
}
void handler(int arg){
wait(NULL);
}
//TCP通信
int main(){
int serverfd,connfd;
struct sockaddr_in serveraddr,clientaddr;
socklen_t len = sizeof(SA);
int cmd;
char clean[SIZE]={0};
pid_t pid;
ssize_t bytes;
//打开数据库
sqlite3 *pdb;
int ret;
ret = sqlite3_open(PATH_DATA,&pdb);
if(ret != SQLITE_OK){
printf("sqlite3 error\n");
return -1;
}
//创建套接字
serverfd = socket(AF_INET,SOCK_STREAM,0);
if(serverfd < 0){
printf("fail to socket\n");
}
//绑定套接字
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(8888);
serveraddr.sin_addr.s_addr = inet_addr("192.168.2.128");
if(bind(serverfd,(SA *)&serveraddr,len) < 0){
printf("fail to bind\n");
}
//监听服务端套接字
if(listen(serverfd,10) < 0){
printf("fail to listen\n");
}
signal(SIGCHLD,handler);//处理僵尸进程
MSG pbuf;
while(1){
//创建新的套接字
connfd = accept(serverfd,(SA *)&clientaddr,&len);
if(connfd < 0){
printf("accept\n");
return -1;
}
//创建子进程
pid = fork();
if(pid < 0){
printf("fail to fork\n");
continue;
}
else if(pid == 0){//子进程
close(serverfd);
while(1){
bytes = recv(connfd,&pbuf,LEN_SMG,0);
if(bytes <= 0)
break;
switch(pbuf.type){
case R:
do_register(connfd,&pbuf,pdb);
break;
case L:
do_login(connfd,&pbuf,pdb);
break;
case Q:
do_query(connfd,&pbuf,pdb);
break;
case H:
do_history(connfd,&pbuf,pdb);
break;
default:
break;
}
}
close(connfd);
exit(1);
}
else//父进程退出
close(connfd);
}
return 0;
}
1、登录、注册界面效果:
2、登录时,用户名或密码错误效果:
3、查询单词效果:
4、输入#结束查询,返回上一级菜单:
5、查询历史记录:
6、两个界面退出效果: