电子词典C语言实现详解(基于linux系统构建服务器、客户端、sqlite3数据库)
文章目录
- 前言
- 一 电子词典说明
- 二 整体思路及框架
- 1.一目了然示意图
- 2.客户端框架
- 3.服务器框架
- 三 手撕代码
- 1.头文件及主函数
- 1.1头文件
- 1.2客户端
- 1.3服务器
- 2.注册、登录账号
- 2.1客户端
- 2.2服务器
- 3.查单词、历史记录
- 3.1客户端
- 3.2服务器
- 4.退出
- 4.1客户端
- 4.2服务器
- 四 完整源码
- 五 完结
前言
需要的基础知识:套接字TCP通信(服务器与客户端),多线程进程,sqlite3数据库,标准IO;
如果上面掌握的不太好也没有关系,咱们有操作是不是,细节拉满,也能学会!
套接字通信参考:https://blog.csdn.net/qq_41796226/article/details/126324397
多线程进程参考:https://blog.csdn.net/qq_41796226/article/details/126355786
sqlite3数据库参考:https://blog.csdn.net/qq_34934140/article/details/120361677
咱们做小项目不是为了实现它的功能而拼凑代码,而是为了从中体会编程的逻辑,举一反三,多处应用。
PS:先赞后看好习惯
一 电子词典说明
- 通过构建多线程并发服务器实现一服务器对多客户端;
- 用户信息数据库维护了3个表:
table | 作用 | |
accountInfo | (usr_name varchar(20),password varchar(20)) 记录所有用户的账号和密码 | |
loginLog | (usr_name varchar(20),operation varchar(10),time varchar(40)) 作为用户的登录和退出的日志 | |
queryLog | (usr_name varchar(20),words varchar(20),time varchar(40)) 作为用户的查询日志 | |
- 拒绝同一个用户多地同时登录;
- 兼容用户的错误类型输入;
- 运行时服务器端可实时查看客户端的连接或断开信息;
- 逻辑清晰,拓展其他功能非常方便。
- 词典文件、sqlite3及源码下载:https://download.csdn.net/download/qq_41796226/86406019
操作演示:

二 整体思路及框架
1.一目了然示意图

先了解一下大概框架,后面我们再详细对每个功能的实现做详细介绍:
2.客户端框架
#include "dic.h"
int main(int argc,char* argv[])
{int sockfd = socket(); //创建套接字int ret_connect = connect();//发起三次握手MSG_t message = {0,0,0}; //定义收发的数据包/*typedef struct //定义数据包结构体类型{int type; char name[20]; char data[1024]; }MSG_t;*/while(1){int choose;scanf("%d", choose); //根据打印出来的功能菜单进行选择switch (choose){case 1:do_register(sockfd,&message); //注册账号break;case 2:if(do_login(sockfd,&message)) //登录账号{dictionary(sockfd,&message); //对词典的操作:查单词、查历史记录}break;case 3:do_exit(sockfd,&message); //退出exit(0);}}
}
void dictionary(int sockfd,MSG_t* pbuf)
{while(1){int choose;scanf("%d", choose);switch (choose){case 1:do_query(sockfd,pbuf); //查单词break;case 2:{do_history(sockfd,pbuf); //查看历史查询记录} break;case 3:do_exit(sockfd,pbuf); //退出exit(0);}}
}
3.服务器框架
#include "dic.h"
int main(int argc,char* argv[])
{createDb_table(); //创建或打开数据库,用户信息表,历史记录表int listenfd = socket(AF_INET,SOCK_STREAM,0); //创建监听套接字int ret_bind = bind(); //绑定监听套接字与主机网络地址信息int ret_listen = listen(listenfd,10); //设置监听队列的大小while(1){int newconfd = accept(listenfd,(struct sockaddr*)&buf_addr,&buf_addrlen); //监听等待连接pthread_create(&tid,NULL,dealClient,&newconfd); //创建线程处理新的连接}
}
void* dealClient(void* p) //线程函数入口
{pthread_detach(pthread_self()); //分离线程,线程结束自动释放资源int newconfd = *(int*)p;MSG_t message = {0,0,0};while(1){ssize_t ret_read = read(newconfd,&message,sizeof(MSG_t)); //循环读取客户端发来的数据switch(message.type) //根据数据的 .type 执行相应函数{case R:do_register(newconfd,&message); //注册账号break;case L:do_login(newconfd,&message); //登录账号break;case Q:do_query(newconfd,&message); //查单词break;case H:do_history(newconfd,&message); //查历史记录break;case E:do_exit(newconfd,&message); //退出break;}}
}
三 手撕代码
1.头文件及主函数
1.1头文件
#ifndef DIC_H
#define DIC_H#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #define R 1 // user register
#define L 2 // user login
#define Q 3 // query word
#define H 4 // history record
#define E 5 // exittypedef struct
{int type; char name[20]; char data[1024];
}MSG_t; //统一服务器与客户端通信时收发数据的格式
typedef struct
{int fd;char IP[20];
}ThreadParam_t; //创建线程时给线程的参数类型#endif
1.2客户端
/************************************************************************************************/
/* Electronic Dictionary Client */
/************************************************************************************************/#include "dic.h"
void error_Handling(char* func,int retval); //本猿封装的错误处理函数,为了方便不是很严谨
void do_register(int sockfd,MSG_t* pbuf);
int do_login(int sockfd,MSG_t* pbuf);
void do_query(int sockfd,MSG_t* pbuf);
void do_history(int sockfd,MSG_t* pbuf);
void do_exit(int sockfd,MSG_t* pbuf);
void dictionary(int sockfd,MSG_t* pbuf);
int transform(char* choose); //switch case时兼容用户的错误类型输入int main(int argc,char* argv[])
{if(argc != 3){printf("%s--IP--Port\n",argv[0]); //命令行传参传入服务器的IP和端口号exit(1);}/*创建套接字*/int sockfd = socket(AF_INET,SOCK_STREAM,0); //TCP类型的套接字error_Handling("socket",sockfd);/*连接*/struct sockaddr_in serverAddr; //填充地址信息serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(atoi(argv[2]));serverAddr.sin_addr.s_addr = inet_addr(argv[1]);int ret_connect = connect(sockfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));//发起三次握手error_Handling("connect",ret_connect);MSG_t message = {0,0,0}; //定义并初始化结构体数据while(1){system("clear"); //清屏,使界面更加优雅printf("**********************************************************************\n");printf("* NewFuture Electronic Dictionary *\n");printf("* Version : 0.0.1 *\n");printf("* 1: register 2: login 3: exit *\n");printf("**********************************************************************\n");printf("please choose a mode: ");char choose[100];scanf("%s", choose); //没有定义成int类型是因为用户输入字符或者字符串时会出错switch (transform(choose)) //transform函数把用户输入转换成int类型{case 1:do_register(sockfd,&message); //注册账号break;case 2:if(do_login(sockfd,&message)) //登录账号{dictionary(sockfd,&message);//登录成功则进入词典操作界面}break;case 3:do_exit(sockfd,&message); //退出exit(0);default:{printf("Message:无效的输入\n");break;}}sleep(1);}
}int transform(char* choose) //把用户输入的字符串转换成switch语句使用的整型
{if(!strcmp(choose,"1"))return 1;else if(!strcmp(choose,"2"))return 2;else if(!strcmp(choose,"3"))return 3;else return 0;
}
void error_Handling(char* func,int retval) //不太严谨的通用函数错误处理
{if(retval == -1){perror(func);exit(1);}
}
void createDb_table() //创建或打开1库3表
{ret = sqlite3_open("usrInfo.db",&db); //创建或打开数据库if(ret != 0){perror("sqlite3_open");exit(1);}char* sql = "create table accountInfo(usr_name varchar(20),password varchar(20))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL); //创建账号信息表if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}sql = "create table queryLog(usr_name varchar(20),words varchar(20),time varchar(40))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL); //创建查询历史记录表if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}sql = "create table loginLog(usr_name varchar(20),operation varchar(10),time varchar(40))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL); //创建登录/退出记录表if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}printf("Server_Infomation: The database and tables are ready\n");
}
1.3服务器
/************************************************************************************************/
/* Electronic Dictionary Server */
/************************************************************************************************/#include "dic.h"
void error_Handling(char* func,int retval);
void break_handling(int retval,char* IP,int fd,MSG_t* pbuf); //客户端断开连接处理函数
void do_register(int newconfd,MSG_t* pbuf);
void do_login(int newconfd,MSG_t* pbuf);
void do_query(int newconfd,MSG_t* pbuf);
void do_history(int newconfd,MSG_t* pbuf);
void do_exit(int newconfd,MSG_t* pbuf);
void recordInQueryLog(MSG_t* pbuf); //记录到查询日志
void recordInloginLog(MSG_t* pbuf,char* operation); //记录到登录日志
void createDb_table(); //创建1库3表
int checkLogout(MSG_t* pbuf,int flag); //检查退出时用户的登录信息,或者做多地登录检查int count,row,col,ret; //1:当前连接的客户端数量;2、3:sqlite3_get_table()的参数;4:通用返回值
sqlite3* db = NULL; //多处操作数据库,定义为全局变量
char sql[100] = {}; //数据库接口函数的sql语句
char** result; //sqlite3_get_table()出参的结果集int main(int argc,char* argv[])
{if(argc != 2){printf("%s--Port\n",argv[0]); //命令行传参传入设定的端口号exit(1);}createDb_table(); //创建数据库,账号信息表,查询记录表,登录退出记录表int listenfd = socket(AF_INET,SOCK_STREAM,0);//创建监听套接字error_Handling("socket",listenfd);int on = 1;int ret_set = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//开启地址复用error_Handling("setsockopt",ret_set);struct sockaddr_in serverAddr;serverAddr.sin_family = AF_INET; //IPV4serverAddr.sin_port = htons(atoi(argv[1]));serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); int ret_bind = bind(listenfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));//绑定监听套接字与主机网络地址信息error_Handling("bind",ret_bind);int ret_listen = listen(listenfd,10); //设置监听队列的大小error_Handling("listen",ret_listen);struct sockaddr_in buf_addr; //用于获取客户端的地址信息socklen_t buf_addrlen = sizeof(buf_addr);pthread_t tid; //用于获取线程IDprintf(" -------------The server is running-------------\n\n");while(1){int newconfd = accept(listenfd,(struct sockaddr*)&buf_addr,&buf_addrlen); //阻塞监听等待连接error_Handling("accept",newconfd);//accept返回时,获得一个新的与客户端连接的套接字,创建线程去处理这个连接ThreadParam_t param;param.fd = newconfd;strcpy(param.IP,inet_ntoa(buf_addr.sin_addr));pthread_create(&tid,NULL,dealClient,¶m); //创建线程并传入参数count++; //客户端连接数+1}
}void* dealClient(void* p) //线程函数入口
{pthread_detach(pthread_self());int newconfd = (*(ThreadParam_t*)p).fd;time_t t = time(NULL);printf(" -------------New connection information-------------\n\n");printf(" System time : %s\n",ctime(&t)); //当前系统时间printf(" Establish a connection with the IP : %s\n",(*(ThreadParam_t*)p).IP);//建立连接的IPprintf(" New connected sockfd : %d\n",newconfd); //新建连接的套接字printf(" Processing ThreadID : %lu\n",pthread_self()); //处理连接的线程IDprintf(" The number of currently connected : %d\n\n\n",count); //当前客户端连接数MSG_t message = {0,0,0}; //定义并初始化结构体数据while(1){ssize_t ret_read = read(newconfd,&message,sizeof(MSG_t));//循环读取客户端发来的数据break_handling(ret_read,(*(ThreadParam_t*)p).IP,newconfd,&message);//read返回值为0时,说明客户端断开连接switch(message.type){case R:do_register(newconfd,&message);break;case L:do_login(newconfd,&message);break;case Q:do_query(newconfd,&message);break;case H:do_history(newconfd,&message);break;case E:do_exit(newconfd,&message);break;}}
}
void error_Handling(char* func,int retval) //不太严谨的通用函数错误处理
{if(retval == -1){perror(func);exit(1);}
}
void break_handling(int retval,char* IP,int fd,MSG_t* pbuf)//处理断开连接
{if(retval<0){perror("read");}if(retval == 0){checkLogout(pbuf,0); //检查日志中最后一次操作是登录还是退出,如果是登录,则把退出操作写入日志count--;time_t t = time(NULL);printf(" -------------New Disconnect information-------------\n\n");printf(" System time : %s\n",ctime(&t)); //当前系统时间printf(" Disconnected to IP : %s\n",IP); //断开连接的IPprintf(" The socket is closed : %d\n",fd); //关闭的套接字printf(" ThreadID is terminated : %lu\n",pthread_self()); //结束的线程IDprintf(" The number of currently connected : %d\n\n",count); //当前客户端连接数pthread_exit(NULL); //线程退出}
}
2.注册、登录账号
2.1客户端
void do_register(int sockfd,MSG_t* pbuf)
{pbuf->type = R; //数据类型为R,告诉服务器这是一个注册请求printf("\nInput User Name :");scanf("%s",pbuf->name);printf("Input Password :");scanf("%s",pbuf->data);ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t)); //阻塞等待服务器做注册账号的相关处理error_Handling("read",ret_read);puts(pbuf->data); //read函数返回时,服务器已经完成相关操作并把返回信息放在pbuf->data里
}
int do_login(int sockfd,MSG_t* pbuf) //登录成功返回1,失败返回0
{pbuf->type = L; //数据类型为L,告诉服务器这是一个登录请求printf("\nInput User Name :");scanf("%s",pbuf->name);printf("Input Password :");scanf("%s",pbuf->data);ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);puts(pbuf->data);sleep(1);if(pbuf->type == -1) //如果账号或密码错误,服务器把type置为-1{return 0;}return 1;
}
2.2服务器
void do_register(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from accountInfo where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row > 0) //对用户输入的名字进行查找,已存在则不允许注册{strcpy(pbuf->data,"Message:账号已存在,重新注册!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}else //不存在,则把用户输入的账号和密码写入数据库{bzero(sql,sizeof(sql));sprintf(sql,"insert into accountInfo values('%s','%s')",pbuf->name,pbuf->data);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);strcpy(pbuf->data,"Message:注册成功!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}
}
void do_login(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from accountInfo where usr_name = '%s' and p-a-s-s-w-o-r-d = '%s'",pbuf->name,pbuf->data); //注意,由于网页发布文章检测的原因,自行将上面password中短线去掉sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row == 0) //对用户输入的账号密码进行查找匹配,匹配失败则不允许登录{strcpy(pbuf->data,"Message:账号不存在或密码错误!");pbuf->type = -1;ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}else if(row == 1) //匹配成功{if( checkLogout(pbuf,1) ) //检查该账号是否已经登陆{strcpy(pbuf->data,"Message:该账号已在另外一台设备上登录!");pbuf->type = -1;}else{recordInloginLog(pbuf,"login"); //将此次登录操作写入用户登录日志strcpy(pbuf->data,"Message:登录成功!");} ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}
}
int checkLogout(MSG_t* pbuf,int flag) // flag=1:登陆时调用,检查账号是否重复登录
{ // flag=0:退出或断开连接时调用,检查上次操作是登录还是退出if(strlen(pbuf->name) == 0) //客户端没有尝试登录过return 0;sprintf(sql,"select * from loginLog where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row == 0) //没有登录成功时退出,且之前没有记录过这个名字return 0;if(strcmp(result[(row+1)*col-2],"login") == 0){ if(flag) return 1;recordInloginLog(pbuf,"logout"); //将此次退出操作写入日志}return 0;
}
void recordInloginLog(MSG_t* pbuf,char* operation)
{char time_q[40];time_t t = time(NULL);sprintf(time_q,"%s",ctime(&t));sprintf(sql,"insert into loginLog values('%s','%s','%s')",pbuf->name,operation,time_q);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);
}
3.查单词、历史记录
3.1客户端
void dictionary(int sockfd,MSG_t* pbuf)
{while(1){system("clear"); //清屏使界面更加优雅printf("**********************************************************************\n");printf("* NewFuture Electronic Dictionary *\n");printf("* Version : 0.0.1 *\n");printf("* 1: Words_Query 2: History_Reco 3: exit *\n");printf("**********************************************************************\n");printf("please choose a mode: ");char choose[1000];scanf("%s", choose); //没有定义成int类型是因为用户输入字符或者字符串时会出错switch (transform(choose)) //transform函数把用户输入转换成int类型{case 1:do_query(sockfd,pbuf); break;case 2:{do_history(sockfd,pbuf);} break;case 3:do_exit(sockfd,pbuf);exit(0);default:{printf("Message:无效的输入\n");sleep(1);}break;}}
}
void do_query(int sockfd,MSG_t* pbuf)
{pbuf->type = Q; //数据类型为Q,告诉服务器这是一个查单词请求while(1){printf("Input words (quit by '#') :");scanf("%s",pbuf->data); //把输入的单词传给服务器if(strcmp(pbuf->data,"#") == 0) //输入"#"表示结束查单词break;ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);putchar('\n');puts(pbuf->data);}
}
void do_history(int sockfd,MSG_t* pbuf)
{pbuf->type = H; //数据类型为H,告诉服务器这是一个查历史记录请求ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read;int i = 0;while( ret_read = read(sockfd,pbuf,sizeof(MSG_t)) ) //循环读{error_Handling("read",ret_read);if( strcmp(pbuf->data,"end") == 0) //服务器最后发一个"end"作为结尾break;printf("%s ",pbuf->data);i++;if(i%3 == 0) //换行使界面更加优雅printf("\n");}printf("Message:按回车返回上一层\n");getchar(); //先获取上次scanf没有读走的回车getchar(); //获取回车,结束历史记录的查看
}
3.2服务器
void do_query(int newconfd,MSG_t* pbuf)
{FILE *fp = fopen("dict.txt","r"); //打开词典文件if(fp == NULL){perror("open");exit(-1);}char word[50] = "\0"; char buf[100] = "\0";int flag = 0;while(fgets(buf,sizeof(buf),fp)!=NULL) //查询算法这里用的是顺序查找,可以升级为其他高效的算法{ //应当根据文件内容的特点制定合适的查询算法bzero(word,sizeof(word));int i = 0;while(buf[i]>='a'&&buf[i]<='z'){word[i] = buf[i++];}if(strcmp(word,pbuf->data)==0){flag = 1;break;}}if(!flag) //没有查询到{strcpy(pbuf->data,"Message:Sorry,Word Not found");}else //查询到{recordInQueryLog(pbuf); //写入查询历史记录strcpy(pbuf->data,buf);} fclose(fp);ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}
void recordInQueryLog(MSG_t* pbuf)
{char time_q[40];time_t t = time(NULL);sprintf(time_q,"%s",ctime(&t));sprintf(sql,"insert into queryLog values('%s','%s','%s')",pbuf->name,pbuf->data,time_q);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);
}
void do_history(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from queryLog where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL); //查找出该用户的所有查询记录int i;for(i=0;i<(row+1)*col;i++) //row和col不包含字段,但结果集result中包含了字段{strcpy(pbuf->data,result[i]);ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}strcpy(pbuf->data,"end"); //最后发送"end"作为结尾表示发送完毕ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}
4.退出
4.1客户端
void do_exit(int sockfd,MSG_t* pbuf)
{pbuf->type = E; //数据类型为E,告诉服务器这是一个退出请求ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);puts(pbuf->data);
}
4.2服务器
void do_exit(int newconfd,MSG_t* pbuf)
{ //这里没有操作登录日志,因为客户端退出时即断开连接,在断开连接函数中也会写入日志strcpy(pbuf->data,"Message:退出成功!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}
四 完整源码
/**********************************dic.h**********************************/
#ifndef DIC_H
#define DIC_H#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include #define R 1 // user register
#define L 2 // user login
#define Q 3 // query word
#define H 4 // history record
#define E 5 // exittypedef struct
{int type; char name[20]; char data[1024];
}MSG_t;
typedef struct
{int fd;char IP[20];
}ThreadParam_t;#endif/**********************************client.c**********************************/
/************************************************************************************************/
/* Electronic Dictionary Client */
/************************************************************************************************/#include "dic.h"
void error_Handling(char* func,int retval);
void do_register(int sockfd,MSG_t* pbuf);
int do_login(int sockfd,MSG_t* pbuf);
void do_query(int sockfd,MSG_t* pbuf);
void do_history(int sockfd,MSG_t* pbuf);
void do_exit(int sockfd,MSG_t* pbuf);
void dictionary(int sockfd,MSG_t* pbuf);
int transform(char* choose);int main(int argc,char* argv[])
{if(argc != 3){printf("%s--IP--Port\n",argv[0]);exit(1);}/*创建套接字*/int sockfd = socket(AF_INET,SOCK_STREAM,0);error_Handling("socket",sockfd);/*连接*/struct sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(atoi(argv[2]));serverAddr.sin_addr.s_addr = inet_addr(argv[1]);int ret_connect = connect(sockfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));error_Handling("connect",ret_connect);MSG_t message = {0,0,0};while(1){system("clear");printf("**********************************************************************\n");printf("* NewFuture Electronic Dictionary *\n");printf("* Version : 0.0.1 *\n");printf("* 1: register 2: login 3: exit *\n");printf("**********************************************************************\n");printf("please choose a mode: ");char choose[100];scanf("%s", choose);switch (transform(choose)){case 1:do_register(sockfd,&message);break;case 2:if(do_login(sockfd,&message)){dictionary(sockfd,&message);}break;case 3:do_exit(sockfd,&message);exit(0);default:{printf("Message:无效的输入\n"); break;}}sleep(1);}
}
void do_register(int sockfd,MSG_t* pbuf)
{pbuf->type = R;printf("\nInput User Name :");scanf("%s",pbuf->name);printf("Input Password :");scanf("%s",pbuf->data);ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);puts(pbuf->data);
}
int do_login(int sockfd,MSG_t* pbuf)
{pbuf->type = L;printf("\nInput User Name :");scanf("%s",pbuf->name);printf("Input Password :");scanf("%s",pbuf->data);ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);puts(pbuf->data);sleep(1);if(pbuf->type == -1){return 0;}return 1;
}
void dictionary(int sockfd,MSG_t* pbuf)
{while(1){system("clear");printf("**********************************************************************\n");printf("* NewFuture Electronic Dictionary *\n");printf("* Version : 0.0.1 *\n");printf("* 1: Words_Query 2: History_Reco 3: exit *\n");printf("**********************************************************************\n");printf("please choose a mode: ");char choose[1000];scanf("%s", choose);switch (transform(choose)){case 1:do_query(sockfd,pbuf); break;case 2:{do_history(sockfd,pbuf);} break;case 3:do_exit(sockfd,pbuf);exit(0);default:{printf("Message:无效的输入\n");sleep(1);}break;}}
}void do_query(int sockfd,MSG_t* pbuf)
{pbuf->type = Q;while(1){printf("Input words (quit by '#') :");scanf("%s",pbuf->data);if(strcmp(pbuf->data,"#") == 0)break;ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);putchar('\n');puts(pbuf->data);}
}
void do_history(int sockfd,MSG_t* pbuf)
{pbuf->type = H;ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read;int i = 0;while( ret_read = read(sockfd,pbuf,sizeof(MSG_t)) ){error_Handling("read",ret_read);if( strcmp(pbuf->data,"end") == 0)break;printf("%s ",pbuf->data);i++;if(i%3 == 0)printf("\n");}printf("Message:按回车返回上一层\n");getchar();getchar();
}int transform(char* choose)
{if(!strcmp(choose,"1"))return 1;else if(!strcmp(choose,"2"))return 2;else if(!strcmp(choose,"3"))return 3;else return 0;
}void error_Handling(char* func,int retval)
{if(retval == -1){perror(func);exit(1);}
}void do_exit(int sockfd,MSG_t* pbuf)
{pbuf->type = E;ssize_t ret_write = write(sockfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);ssize_t ret_read = read(sockfd,pbuf,sizeof(MSG_t));error_Handling("read",ret_read);puts(pbuf->data);
}/**********************************server.c**********************************/
/************************************************************************************************/
/* Electronic Dictionary Server */
/************************************************************************************************/#include "dic.h"
void error_Handling(char* func,int retval);
void break_handling(int retval,char* IP,int fd,MSG_t* pbuf);
void do_register(int newconfd,MSG_t* pbuf);
void do_login(int newconfd,MSG_t* pbuf);
void do_query(int newconfd,MSG_t* pbuf);
void do_history(int newconfd,MSG_t* pbuf);
void do_exit(int newconfd,MSG_t* pbuf);
void recordInQueryLog(MSG_t* pbuf);
void recordInloginLog(MSG_t* pbuf,char* operation);
void createDb_table();
int checkLogout(MSG_t* pbuf,int flag);int count,row,col,ret;
sqlite3* db = NULL;
char sql[100] = {};
char** result;void* dealClient(void* p)
{pthread_detach(pthread_self());int newconfd = (*(ThreadParam_t*)p).fd;time_t t = time(NULL);printf(" -------------New connection information-------------\n\n");printf(" System time : %s\n",ctime(&t));printf(" Establish a connection with the IP : %s\n",(*(ThreadParam_t*)p).IP);printf(" New connected sockfd : %d\n",newconfd);printf(" Processing ThreadID : %lu\n",pthread_self());printf(" The number of currently connected : %d\n\n\n",count);MSG_t message = {0,0,0};while(1){ssize_t ret_read = read(newconfd,&message,sizeof(MSG_t));break_handling(ret_read,(*(ThreadParam_t*)p).IP,newconfd,&message);switch(message.type){case R:do_register(newconfd,&message);break;case L:do_login(newconfd,&message);break;case Q:do_query(newconfd,&message);break;case H:do_history(newconfd,&message);break;case E:do_exit(newconfd,&message);break;}}
}int main(int argc,char* argv[])
{if(argc != 2){printf("%s--Port\n",argv[0]);exit(1);}/*创建数据库,用户信息表,历史记录表*/createDb_table();/*创建套接字——监听套接字*/int listenfd = socket(AF_INET,SOCK_STREAM,0);error_Handling("socket",listenfd);/*绑定监听套接字与主机网络地址信息*/int on = 1;int ret_set = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));error_Handling("setsockopt",ret_set);struct sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(atoi(argv[1]));serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);int ret_bind = bind(listenfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));error_Handling("bind",ret_bind);/*设置监听队列的大小*/int ret_listen = listen(listenfd,10);error_Handling("listen",ret_listen);/*监听等待连接*/struct sockaddr_in buf_addr;socklen_t buf_addrlen = sizeof(buf_addr);pthread_t tid;printf(" -------------The server is running-------------\n\n");while(1){int newconfd = accept(listenfd,(struct sockaddr*)&buf_addr,&buf_addrlen);error_Handling("accept",newconfd);ThreadParam_t param;param.fd = newconfd;strcpy(param.IP,inet_ntoa(buf_addr.sin_addr));pthread_create(&tid,NULL,dealClient,¶m);count++;}}
void createDb_table()
{/*创建库*/ret = sqlite3_open("usrInfo.db",&db);if(ret != 0){perror("sqlite3_open");exit(1);}/*创建表*/char* sql = "create table accountInfo(usr_name varchar(20),password varchar(20))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL);if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}sql = "create table queryLog(usr_name varchar(20),words varchar(20),time varchar(40))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL);if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}sql = "create table loginLog(usr_name varchar(20),operation varchar(10),time varchar(40))";ret = sqlite3_exec(db,sql,NULL,NULL,NULL);if(ret != 0){printf("Server_Infomation: %s\n",sqlite3_errmsg(db));}printf("Server_Infomation: The database and tables are ready\n");
}
void do_register(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from accountInfo where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row > 0){strcpy(pbuf->data,"Message:账号已存在,重新注册!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}else{bzero(sql,sizeof(sql));sprintf(sql,"insert into accountInfo values('%s','%s')",pbuf->name,pbuf->data);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);strcpy(pbuf->data,"Message:注册成功!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}
}
void do_login(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from accountInfo where usr_name = '%s' and p-a-s-s-w-o-r-d = '%s'",pbuf->name,pbuf->data);//注意,由于网页发布文章检测的原因,自行将上面password中短线去掉sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row == 0){strcpy(pbuf->data,"Message:账号不存在或密码错误!");pbuf->type = -1;ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}else if(row == 1){if( checkLogout(pbuf,1) ){strcpy(pbuf->data,"Message:该账号已在另外一台设备上登录!");pbuf->type = -1;}else{recordInloginLog(pbuf,"login"); strcpy(pbuf->data,"Message:登录成功!");} ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}
}
void do_query(int newconfd,MSG_t* pbuf)
{FILE *fp = fopen("dict.txt","r");if(fp == NULL){perror("open");exit(-1);}char buf[100] = "\0";char word[50] = "\0";int flag = 0;while(fgets(buf,sizeof(buf),fp)!=NULL){bzero(word,sizeof(word));int i = 0;while(buf[i]>='a'&&buf[i]<='z'){word[i] = buf[i++];}if(strcmp(word,pbuf->data)==0){flag = 1;break;}}if(!flag){strcpy(pbuf->data,"Message:Sorry,Word Not found");}else{recordInQueryLog(pbuf);strcpy(pbuf->data,buf);} fclose(fp);ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}
void do_history(int newconfd,MSG_t* pbuf)
{sprintf(sql,"select * from queryLog where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL);int i;for(i=0;i<(row+1)*col;i++){strcpy(pbuf->data,result[i]);ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);}strcpy(pbuf->data,"end");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}void recordInloginLog(MSG_t* pbuf,char* operation)
{char time_q[40];time_t t = time(NULL);sprintf(time_q,"%s",ctime(&t));sprintf(sql,"insert into loginLog values('%s','%s','%s')",pbuf->name,operation,time_q);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);
}void recordInQueryLog(MSG_t* pbuf)
{char time_q[40];time_t t = time(NULL);sprintf(time_q,"%s",ctime(&t));sprintf(sql,"insert into queryLog values('%s','%s','%s')",pbuf->name,pbuf->data,time_q);ret = sqlite3_exec(db,sql,NULL,NULL,NULL);
}int checkLogout(MSG_t* pbuf,int flag)
{if(strlen(pbuf->name) == 0) return 0;sprintf(sql,"select * from loginLog where usr_name = '%s'",pbuf->name);ret = sqlite3_get_table(db,sql,&result,&row,&col,NULL);if(row == 0) return 0;if(strcmp(result[(row+1)*col-2],"login") == 0){ if(flag)return 1;recordInloginLog(pbuf,"logout");}return 0;
}
void break_handling(int retval,char* IP,int fd,MSG_t* pbuf)
{if(retval<0){perror("read");}if(retval == 0){checkLogout(pbuf,0);count--;time_t t = time(NULL);printf(" -------------New Disconnect information-------------\n\n");printf(" System time : %s\n",ctime(&t));printf(" Disconnected to IP : %s\n",IP);printf(" The socket is closed : %d\n",fd);printf(" ThreadID is terminated : %lu\n",pthread_self());printf(" The number of currently connected : %d\n\n",count);pthread_exit(NULL);}
}
void do_exit(int newconfd,MSG_t* pbuf)
{strcpy(pbuf->data,"Message:退出成功!");ssize_t ret_write = write(newconfd,pbuf,sizeof(MSG_t));error_Handling("write",ret_write);
}
void error_Handling(char* func,int retval)
{if(retval == -1){perror(func);exit(1);}
}
五 完结
高端IT培训来华清远见 www.hqyj.com


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
