项目要求:
-
登录注册功能,不能重复登录,重复注册
-
单词查询功能
-
历史记录功能,存储单词,意思,以及查询时间
-
基于TCP,支持多客户端连接
-
采用数据库保存用户信息与历史记录
-
将dict.txt的数据导入到数据库中保存。
-
按下ctrl+c退出客户端后,注销该客户端的登录信息
格式要求:
-
main函数只跑逻辑,不允许跑功能代码
-
功能代码封装成函数
服务器代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>
#include <time.h>
#include <stdlib.h>
#define ERR_MSG(msg) {printf("Line=%d\n",__LINE__);perror(msg);}
#define IP "0.0.0.0"
#define PORT 8888
struct ifo_client ///传参结构体
{
int cfd;
struct sockaddr_in cin;
sqlite3 * db;
};
void do_insert(sqlite3 *db) //导入词典文本函数
{
printf("正在导入电子词典,请稍候\n");
FILE* fd;
fd=fopen("./dict.txt","r");
if (fd==NULL)
{
perror("fopen");
return ;
}
char *p;
char arr[128]="";
char word[128]="";
char mean[128]="";
char crr[128]="";
int j;
int i;
while(1)
{
bzero(arr,sizeof(arr));
bzero(word,sizeof(word));
bzero(mean,sizeof(mean));
p=fgets(arr,sizeof(arr),fd);
arr[strlen(arr)-1]=0;
for(j=0;j<128;j++)
{
if (arr[j]==' ')
{
break;
}
}
for (i=0; i<128; i++)
{
if (arr[i]=='.'&&(i>j))
{
for (j=i; j>0; j--)
{
if (arr[j]==' ')
{
strcpy(mean,arr+j+1);
break ;
}
}
break;
}
}
arr[j]=0;
strcpy(word,arr);
for(int k=(strlen(word)-1);k>0;k--)
{
if(word[k]!=' ')
{
word[k+1]=0;
break;
}
}
if (strlen(word)==0)
{
break;
}
sprintf(crr,"insert into dictionary values (\"%s\",\"%s\")",word,mean);
char * errmsg;
if (sqlite3_exec(db,crr,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return;
}
if (p==NULL)
{
if (errno!=0)
{
ERR_MSG("fgets");
return ;
}
}
}
fclose(fd);
printf("insert dictionary success\n");
}
void *callback(void *pp) //回调函数
{
struct ifo_client ifo=*(struct ifo_client*)pp;
int cfd=ifo.cfd;
struct sockaddr_in connect_done=ifo.cin;
sqlite3 *db=ifo.db;
char buf[1024]="";
char arr[128]="";
char brr[128]="";
char insert[256]="";
ssize_t res=0;
int row,column;
while(1)
{
char **pres;
bzero(buf,sizeof(buf));
bzero(arr,sizeof(arr));
bzero(brr,sizeof(brr));
bzero(insert,sizeof(insert));
res=recv(cfd,buf,sizeof(buf),0);
char * errmsg;
if (res<0)
{
ERR_MSG("recv");
return NULL;
}
if (buf[0]=='N')
{
// pres=NULL;
sprintf(arr,"%s",buf+1);
sprintf(brr,"%s",buf+2+strlen(arr));
sprintf(insert,"insert into passwd_name values (\"%s\",\"%s\",\"offline\")",arr,brr);
if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
{
//有相同用户名注册失败返回WRONG
bzero(buf,sizeof(buf));
sprintf(buf,"WRONG USER");
send(cfd,buf,strlen(buf),0);
continue;
}
//成功返回OK
bzero(buf,sizeof(buf));
sprintf(buf,"ENROLL SUCCESS");
send(cfd,buf,strlen(buf),0);
continue;
}
else if (buf[0]=='L')
{
sprintf(insert,"select * from passwd_name where name=\"%s\"",buf+1);
if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
return NULL;
}
if(row==0)
{
//说明用户不存在需要注册
bzero(buf,sizeof(buf));
sprintf(buf,"NO USER");
send(cfd,buf,strlen(buf),0);
bzero(buf,sizeof(buf));
sqlite3_free_table(pres);
continue;
}
if (strcmp(*(pres+4),buf+2+strlen(buf+1))!=0)
{
//说明密码错误
bzero(buf,sizeof(buf));
sprintf(buf,"WRONG PASSWD");
send(cfd,buf,strlen(buf),0);
bzero(buf,sizeof(buf));
sqlite3_free_table(pres);
continue;
}
if(strcmp(*(pres+5),"online")==0)
{
//已在其他地方登录
bzero(buf,sizeof(buf));
sprintf(buf,"Repeat logins");
send(cfd,buf,strlen(buf),0);
bzero(buf,sizeof(buf));
sqlite3_free_table(pres);
continue;
}
//将状态修改为在线
bzero(insert,sizeof(insert));
sprintf(insert,"update passwd_name set stat=\"online\" where name=\"%s\"",buf+1);
if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
bzero(buf,sizeof(buf));
sprintf(buf,"LOGIN SUCCESS");
send(cfd,buf,strlen(buf),0);
sqlite3_free_table(pres);
continue;
}
else if(buf[0]=='S')
{
sprintf(insert,"select * from dictionary where word=\"%s\"",buf+2+strlen(buf+1));
if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
return NULL;
}
if(row==0)
{
//说明单词不存在
bzero(buf,sizeof(buf));
sprintf(buf,"NO WORD");
send(cfd,buf,strlen(buf),0);
continue;
}
//记录查询时间
time_t t;
time(&t);
struct tm *time_cur;
time_cur=localtime(&t);
char time[128]="";
sprintf(time,"[%d-%02d-%02d %02d-%02d-%02d]",time_cur->tm_year+1900,time_cur->tm_mon+1,time_cur->tm_mday,time_cur->tm_hour,time_cur->tm_min,time_cur->tm_sec);
bzero(arr,sizeof(arr));
for(int i=0;i<((row+1)*column);i++)
{
sprintf(arr+strlen(arr),"%s",pres[i]);
arr[strlen(arr)]='*';
if(i%column==(column-1))
{
//插入到历史记录中
bzero(insert,sizeof(insert));
if(i>1)
{
sprintf(insert,"insert into history values (\"%s\",\"%s\",\"%s\",\"%s\")",buf+1,pres[i-1],pres[i],time);
if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
}
}
}
//发送给客户端
send(cfd,arr,strlen(arr),0);
bzero(arr,sizeof(arr));
sqlite3_free_table(pres);
continue;
}
else if(buf[0]=='H')
{
bzero(insert,sizeof(insert));
sprintf(insert,"select word, mean, time from history where name=\"%s\"",buf+1);
if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
return NULL;
}
if(row==0)
{
//没有记录
bzero(buf,sizeof(buf));
sprintf(buf,"NO HISTORY");
send(cfd,buf,strlen(buf),0);
continue;
}
bzero(arr,sizeof(arr));
for(int i=0;i<((row+1)*column);i++)
{
sprintf(arr+strlen(arr),"%s",pres[i]);
arr[strlen(arr)]='*';
}
send(cfd,arr,strlen(arr),0);
bzero(arr,sizeof(arr));
sqlite3_free_table(pres);
continue;
}
else if (buf[0]=='E')
{
if(strlen(buf)>1)
{
bzero(insert,sizeof(insert));
sprintf(insert,"update passwd_name set stat=\"offline\" where name=\"%s\"",buf+1);
if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
printf("[%s:%d] %s 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port),buf+1);
continue;
}
printf("[%s:%d] 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port));
continue;
}
else if(buf[0]=='F')
{
if(strlen(buf)>1)
{
bzero(insert,sizeof(insert));
sprintf(insert,"update passwd_name set stat=\"offline\" where name=\"%s\"",buf+1);
if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
printf("[%s:%d] %s 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port),buf+1);
break;
}
printf("[%s:%d] 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port));
break;
}
}
close(cfd);
pthread_exit(NULL);
}
sqlite3 * db_create() //打开数据库函数
{
//判断库是否存在,存在直接删除
if(access("./dict.db",F_OK)==0)
{
unlink("./dict.db");
}
//打开数据库
sqlite3 *db;
if(sqlite3_open("./dict.db",&db)!=SQLITE_OK)
{
printf("errcode=%d errmsg=%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
return NULL;
}
//创建存储电子词典文本的表
char * p="create table if not exists dictionary (word char,mean char)";
char * errmsg;
if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
//创建存储名字,密码,状态的表,并且把名字设为主键
p="create table if not exists passwd_name (name char primary key,passwd char,stat char)";
if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
//创建存储名字,单词,单词意思,查询时间的表
p="create table if not exists history (name char,word char,mean char,time char)";
if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
{
fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
return NULL;
}
return db;
}
int socket_func()
{
int sfd=socket(AF_INET,SOCK_STREAM,0);
if (sfd<0)
{
ERR_MSG("socket");
return -1;
}
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if (bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
if (listen(sfd,128)<0)
{
ERR_MSG("listen");
return -1;
}
return sfd;
}
int main(int argc, const char *argv[])
{
//打开数据库,并且制作表
sqlite3 * db = db_create();
//导入电子词典文本
do_insert(db);
//创建传参结构体
struct ifo_client ifo;
ifo.db=db;
int sfd=socket_func();
int cfd;
struct sockaddr_in connect_done;
socklen_t len=sizeof(connect_done);
pthread_t pt_1;
while(1)
{
cfd=accept(sfd,(struct sockaddr*)&connect_done,&len);
if (cfd<0)
{
ERR_MSG("accept");
return -1;
}
ifo.cfd=cfd;
ifo.cin=connect_done;
if (pthread_create(&pt_1,NULL,callback,(void*)&ifo)!=0)
{
ERR_MSG("pthread_create");
return -1;
}
//分离线程
pthread_detach(pt_1);
}
close(sfd);
return 0;
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#define ERR_MSG(msg) {printf("Line=%d\n",__LINE__);perror(msg);}
#define IP "0.0.0.0"
#define PORT 8888
typedef void (*sighandler_t)(int);
char name[64]=""; //全局变量
int sfd;
int deal_func();
int enroll() // 注册函数
{
char name_1[64]="";
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
ssize_t res=0;
bzero(name_1,sizeof(name_1));
printf("请输入账户名\n");
fgets(name_1,sizeof(name_1),stdin);
name_1[strlen(name_1)-1]=0;
printf("请输入账户密码\n");
fgets(passwd,sizeof(passwd),stdin);
passwd[strlen(passwd)-1]=0;
//拼接
insert[0]='N';
sprintf(insert+1,"%s",name_1);
insert[1+strlen(name_1)]=0;
sprintf(insert+2+strlen(name_1),"%s",passwd);
send(sfd,insert,strlen(name_1)+strlen(passwd)+2,0);
res=recv(sfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return -1;
}
else if(res==0)
{
printf("server offline\n");
return 0;
}
else
{
if(strcmp(buf,"WRONG USER")==0)
{
printf("\t\tUser already exists\n");
bzero(insert,sizeof(insert));
bzero(name_1,sizeof(name_1));
bzero(passwd,sizeof(passwd));
bzero(buf,sizeof(buf));
// 名字不对重新输入
enroll();
}
else if(strcmp(buf,"ENROLL SUCCESS")==0)
{
printf("\t\tEnroll success\n");
bzero(name_1,sizeof(name_1));
}
}
}
int logins_fun(int*flags) //登录函数
{
if (*flags!=0)
{
printf("一个终端只能登陆一个\n");
return 0;
}
bzero(name,sizeof(name));
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
ssize_t res=0;
printf("请输入账户名\n");
fgets(name,sizeof(name),stdin);
name[strlen(name)-1]=0;
printf("请输入账户密码\n");
fgets(passwd,sizeof(passwd),stdin);
passwd[strlen(passwd)-1]=0;
insert[0]='L';
sprintf(insert+1,"%s",name);
insert[1+strlen(name)]=0;
sprintf(insert+2+strlen(name),"%s",passwd);
res=send(sfd,insert,strlen(name)+strlen(passwd)+2,0);
if(res<=0)
{
ERR_MSG("send");
return -1;
}
bzero(buf,sizeof(buf));
res=recv(sfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return -1;
}
else if(res==0)
{
printf("server offline\n");
return 0;
}
else
{
if(strcmp(buf,"NO USER")==0)
{
bzero(name,sizeof(name));
printf("Username not exists\n");
R:
printf("*************************\n");
printf("*******1.重新登录********\n");
printf("*******2.重新注册********\n");
printf("*******3.回主菜单********\n");
printf("*************************\n");
int c;
scanf("%d",&c);
getchar();
if(c==1)
{
bzero(name,sizeof(name));
logins_fun(flags);
}
else if(c==2)
{
bzero(name,sizeof(name));
enroll();
}
else if(c==3)
{
bzero(name,sizeof(name));
return -1;
}
else
{
printf("输入错误,重新选择\n");
goto R;
}
}
else if(strcmp(buf,"WRONG PASSWD")==0)
{
printf("密码错误,请重新登录\n");
bzero(name,sizeof(name));
logins_fun(flags);
}
else if(strcmp(buf,"Repeat logins")==0)
{
printf("已在其他地方登录\n");
bzero(name,sizeof(name));
return -1;
}
else if(strcmp(buf,"LOGIN SUCCESS")==0)
{
printf("Login success\n");
*flags=1;
deal_func();
return 2;
}
}
}
int return_func() //退出函数
{
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
if(strlen(name)==0)
{
bzero(insert,sizeof(insert));
insert[0]='F';
send(sfd,insert,1,0);
return 0;
}
else
{
bzero(insert,sizeof(insert));
insert[0]='F';
sprintf(insert+1,"%s",name);
send(sfd,insert,sizeof(insert),0);
return 0;
}
return 0;
}
int search_func() //查找函数
{
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
ssize_t res=0;
int i=0;
bzero(insert,sizeof(insert));
bzero(buf,sizeof(buf));
insert[0]='S';
sprintf(insert+1,"%s",name);
insert[1+strlen(name)]=0;
printf("请输入单词\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
sprintf(insert+2+strlen(name),"%s",buf);
send(sfd,insert,sizeof(insert),0);
bzero(buf,sizeof(buf));
res=recv(sfd,buf,sizeof(buf),0);
if (res<0)
{
ERR_MSG("recv");
return-1;
}
if (res==0)
{
return -1;
}
if (strcmp(buf,"NO WORD")==0)
{
printf("没有这个单词,请重新输入\n");
search_func();
}
int count=-1;
int a=strlen(buf);
int temp=0;
for(i=0;i<a;i++)
{
if(buf[i]=='*')
{
buf[i]=0;
printf("%s\t",buf+count+1);
count=i;
if(temp%2==1)
{
putchar(10);
}
temp++;
}
}
}
int history_func() //历史记录函数
{
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
ssize_t res=0;
int i=0;
insert[0]='H';
sprintf(insert+1,"%s",name);
send(sfd,insert,strlen(name)+1,0);
bzero(buf,sizeof(buf));
res=recv(sfd,buf,sizeof(buf),0);
if (res<0)
{
ERR_MSG("recv");
return-1;
}
if (strcmp(buf,"NO HISTORY")==0)
{
printf("没有历史记录\n");
return 2;
}
int b=-1;
int a1=strlen(buf);
int temp1=0;
for(i=0;i<a1;i++)
{
if(buf[i]=='*')
{
buf[i]=0;
printf("%s\t",buf+b+1);
b=i;
if(temp1%3==2)
{
putchar(10);
}
temp1++;
}
}
}
int socket_func() //连接函数
{
sfd=socket(AF_INET,SOCK_STREAM,0);
if (sfd<0)
{
ERR_MSG("socket");
return_func();
}
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return_func();
}
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=inet_addr(IP);
if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("connect");
return_func();
}
}
int deal_func()
{
while(1)
{
system("clear");
printf("*************************\n");
printf("**********1.查询*********\n");
printf("********2.历史记录*******\n");
printf("********3.回上一级*******\n");
printf("*************************\n");
int d;
scanf("%d",&d);
getchar();
switch(d)
{
case 1:
search_func();
break;
case 2:
history_func();
break;
case 3:
return 0;
default :
printf("输入错误,请重新输入\n");
break;
}
printf("输入任意字符清屏\n");
while(getchar()!=10);
}
}
//信号处理函数
void handler(int sig)
{
return_func();
exit(-1);
}
int return_only_cur_log(int *flags) //当前登录退出
{
if(*flags==0)
{
printf("当前无登录\n");
return 0;
}
char insert[128]="";
char passwd[64]="";
char buf[1024]="";
bzero(insert,sizeof(insert));
insert[0]='E';
sprintf(insert+1,"%s",name);
send(sfd,insert,sizeof(insert),0);
bzero(name,sizeof(name));
*flags=0;
return 0;
}
int main(int argc, const char *argv[])
{
int flags=0;
sighandler_t s = signal(2, handler);
if(SIG_ERR == s)
{
ERR_MSG("signal");
return -1;
}
socket_func();
while(1)
{
system("clear");
printf("*************************\n");
printf("**********1.注册*********\n");
printf("**********2.登录*********\n");
printf("****3.仅退出,当前登录***\n");
printf("**********4.退出*********\n");
printf("*************************\n");
printf("请选择功能\n");
int num;
scanf("%d",&num);
getchar();
switch(num)
{
case 1:
enroll();
break;
case 2:
logins_fun(&flags);
break;
case 3:
return_only_cur_log(&flags);
break;
case 4:
return_func();
return 0;
default :
printf("输入错误,请重新输入\n");
break;
}
printf("输入任意字符清屏\n");
while(getchar()!=10);
}
close(sfd);
return 0;
}