电子词典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:先赞后看好习惯

一 电子词典说明

  1. 通过构建多线程并发服务器实现一服务器对多客户端;
  2. 用户信息数据库维护了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)) 作为用户的查询日志

  1. 拒绝同一个用户多地同时登录;
  2. 兼容用户的错误类型输入;
  3. 运行时服务器端可实时查看客户端的连接或断开信息;
  4. 逻辑清晰,拓展其他功能非常方便。
  5. 词典文件、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,&param); //创建线程并传入参数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,&param);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
在这里插入图片描述

在这里插入图片描述


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

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部