嵌入式linux-sqlite3数据库,多进程并发服务器,在线词典

1,简介:

1,在线词典功能,分为客户端和服务器端
2,客户端有三个模块:注册、登录、查询(查询单词、查询历史记录)
3,服务器端要实现多并发服务器,这里采用多进程并发服务器:注册、登录、查询(查询单词、查询历史记录)
4,用户分为普通用户和管理员用户:管理员用户可以查询所有普通用户的历史记录,还可以看到词库中未定义的单词查询记录,普通用户只能查到自己的,词库中中有定义的记录
5,客户端和服务器通信只要通过一个信息结构体实现:

		typedef struct{
			int flag;  //1:usr or 2:root
			int type;
			char 			name[N];
			char data[256];  //password or word
		}MSG;

6,服务器端有单词词库
7,服务器端有两张数据库表格,一张用来存放用户信息,一张用来存放用户单词查询记录
8,多进程并发服务器要做僵尸进程处理

2,框架图

2.1,客户端框架

在这里插入图片描述

2.1,服务器端框架

在这里插入图片描述

3,代码

3.1,客户端代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h> 
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define N 20

#define R 1
#define L 2
#define Q 3
#define H 4
#define USR  1
#define ROOT 2

typedef struct{
	int flag; //1:usr or 2:root
	int type;
	char name[N];
	char data[256]; //password or word
}MSG;

int do_client(int sockfd);
int do_register(int connectfd,MSG *msg);
int do_login(int connectfd,MSG *msg);
int do_query(int connectfd,MSG *msg);
int do_history(int connectfd,MSG *msg);

int main(int argc, const char *argv[])
{
	int serverfd;
	struct sockaddr_in sin;

	if(argc != 3)
	{
		printf("usage: %s <server_ip> <server_port>\n",argv[0]);
		exit(0);
	}

	//创建流式套接字
	if((serverfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		exit(-1);
	}

	//填充协议地址结构体
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(atoi(argv[2]));
	sin.sin_addr.s_addr = inet_addr(argv[1]); 

	//连接服务器
	if(connect(serverfd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
	{
		perror("connect");
		goto _error1;
	}

	do_client(serverfd);

_error1:
	close(serverfd);
	return 0;
}

int do_client(int sockfd)
{
	MSG msg;
	bzero(&msg,sizeof(msg));

	msg.flag = -1;
	while(msg.flag < 0)
	{
		printf("Please choose permission [1:usr 2:root]");
		if(scanf("%d",&msg.flag) == 0)
			msg.flag = -1;
		while(getchar() != '\n');

		if(msg.flag != 1 && msg.flag != 2)
			msg.flag = -1;
	}

	/* 注册登录模块 */
	while(1)
	{
		msg.type = -1;
		while(msg.type < 0)
		{
			printf("********************************************\n");
			printf("*1:register      2:login     3:quite       *\n");
			printf("********************************************\n");
			printf("PLease choose:");

			if(scanf("%d",&msg.type) == 0)
				msg.type = -1;
			while(getchar() != '\n');
		}

		switch (msg.type)
		{
			case R:
			{
				do_register(sockfd,&msg);
				break;
			}
			case L:
			{
				if(do_login(sockfd,&msg) == 1)
				{
					goto _NEXT;
				}
				break;
			}
			case 3:
			{
				close(sockfd);
				printf("Exit the online dictionary program\n");
				exit(0);
				break;
			}
			default:
				printf("Error cmd.\n");
		}
	}
_NEXT:
	/* 查询模块 */
	while(1)
	{
		msg.type = -1;
		while(msg.type < 0)
		{
			printf("********************************************\n");
			printf("*1:query_word  2:history_record  3:quite   *\n");
			printf("********************************************\n");
			printf("Please choose:");

			if(scanf("%d",&msg.type) == 0)
				msg.type = -1;
			while(getchar() != '\n');
		}
		switch (msg.type)
		{
			case 1:
			{
				msg.type = Q;
				do_query(sockfd,&msg);
				break;
			}
			case 2:
			{
				msg.type = H;
				do_history(sockfd,&msg);
				break;
			}
			case 3:
			{
				close(sockfd);
				printf("Exit the online dictionary program\n");
				exit(0);
				break ;
			}
			default:
				printf("Error cmd.\n");
		}
	}
}

int do_register(int connectfd,MSG *msg)
{
	char str[20];
	int flag;
	
	while(1)
	{
		printf("PLease input name:");
		if(scanf("%s",msg->name)){};
		while(getchar() != '\n');

		flag = -1;
		while(flag < 0)
		{
			printf("PLease input password:");
			if(scanf("%s",str)){};
			while(getchar() != '\n');

			printf("Please confirm password:");
			if(scanf("%s",msg->data)){};
			while(getchar() != '\n');

			if(strncmp(msg->data,str,strlen(str)) == 0)
			{
				flag = 1;
			}
			else
			{
				printf("Entered passwords differ,Please re-enter them.\n");
			}
		}

		if(send(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_register fail to send");
			return -1;
		}

		if(recv(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_register fail to recv");
			return -1;
		}

		if(strncasecmp(msg->data,"ok",strlen("ok")) == 0)
		{
			printf("register success.\n");
			return 1;
			break;
		}
		else
		{
			printf("%s\n",msg->data);
			printf("re-register or cancel [1:re-register 2:cancel]");

			int tmp;
			if(scanf("%d",&tmp)){};
			while(getchar() != '\n');

			if(tmp == 2)
				break;
		}
	}
	return 0;
}
int do_login(int connectfd,MSG *msg)
{
	while(1)
	{
		printf("PLease input name:");
		if(scanf("%s",msg->name)){};
		while(getchar() != '\n');

		printf("PLease input password:");
		if(scanf("%s",msg->data)){};
		while(getchar() != '\n');

		if(send(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_login fail to send");
			return -1;
		}

		if(recv(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_login fail to recv");
			return -1;
		}

		if(strncasecmp(msg->data,"ok",strlen("ok")) == 0)
		{
			printf("login success.\n");
			return 1;
			break;
		}
		else
		{
			printf("%s\n",msg->data);
			printf("re-login or cancel [1:re-login 2:cancel]");

			int tmp;
			if(scanf("%d",&tmp)){};
			while(getchar() != '\n');

			if(tmp == 2)
				break;
		}
	}

	return 0;
}
int do_query(int connectfd,MSG *msg)
{
	while(1)
	{
		printf("Please input query_word('#' exit):");
		if(scanf("%s",msg->data)){};
		while(getchar() != '\n');

		/* 返回上级菜单 */
		if(strncmp(msg->data,"#",strlen("#")) == 0)
		{
			break;
		}

		/* 把含有有查询单词的消息结构体发送给服务器 */
		if(send(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_query fail to send.");
			return -1;
		}

		/* 阻塞接受 */
		if(recv(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_query fail to recv.");
			return -1;
		}

		/* 处理从服务器接受的信息 */
		printf("%s",msg->data);
	}

	return 0;
}
int do_history(int connectfd,MSG *msg)
{
	if(send(connectfd,msg,sizeof(MSG),0) < 0)
	{
		perror("do_history fail to send");
		return -1;
	}
	while(1)
	{
		if(recv(connectfd,msg,sizeof(MSG),0) < 0)
		{
			perror("do_history fail to recv");
			return -1;
		}
#if 1
		if(msg->data[0] == '\0')
		{
			break;
		}
#else
		/* 这种方法是错误的,strlen("\0")是错误的 */
		if(strncmp(msg->data,"\0",strlen("\0")) == 0)
		{
			break;
		}
#endif
		printf("%s\n",msg->data);
	}
	return 0;
}

3.2,服务器端代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h> 
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <netinet/in.h>
#include <sqlite3.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>

#define SERV_PORT 5001
#define BACKLOG 5

#define N 20

#define R 1
#define L 2
#define Q 3
#define H 4
#define USR  1
#define ROOT 2

#define DATABASE "table.db"



typedef struct{
	int flag; //1:usr or 2:root
	int type;
	char name[N];
	char data[256]; //password or word
}MSG;

typedef struct{//回调函数传参
	int fd;
	MSG msg;
}HIS_MSG;

int do_create_table(sqlite3 *db);//判断表格是否存在,不存在则创建
int do_client_handle(int newfd,struct sockaddr_in cin,sqlite3 *db);//子进程处理客户端词典请求操作
int	do_register(int acceptfd,MSG msg,sqlite3 *db);
int do_login(int acceptfd,MSG msg,sqlite3 *db);
int	do_query(int acceptfd,MSG msg,sqlite3 *db);
int	do_history(int acceptfd,MSG msg,sqlite3 *db);
const char *get_date(char * date);//获取本地时间 
int do_searchworld(MSG *msg);//查询词典数据库文件dict.txt中的单词释义
int history_callback(void *his_msg, int f_num, char **f_value, char **f_name);//历史记录查询回调函数

int main(int argc, const char *argv[])
{
	sqlite3 *db;
	int socketfd;
	struct sockaddr_in sin;
	/* 数据库存在则打开,不存在则创建 */
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open database:%s success.\n",DATABASE);
	}
	
	/* 表格不存在则创建数据库表格 */
	if(do_create_table(db) < 0)
	{
		goto _error1;//创建数据库表格失败
	}

	/* 创建流式套接字 */
	if((socketfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("socket");
		goto _error1;
	}
	/* 允许绑定地址快速重用 */ 
	int b_reuse = 1;
	setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));

	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SERV_PORT);
	sin.sin_addr.s_addr = htonl(INADDR_ANY);// 让服务器可以绑定在任意的IP上

	/* 绑定 */
	if(bind(socketfd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
	{
		perror("bind");
		goto _error2;
	}

	/* 将套接字设置为监听模式 */
	if(listen(socketfd,BACKLOG) < 0)
	{
		perror("listen");
		goto _error2;
	}

	signal(SIGCHLD,SIG_IGN) ; //处理僵尸进程

	pid_t pid;
	int status;
	int newfd;
	struct sockaddr_in cin;
	socklen_t cin_add_len = sizeof(cin);
	bzero(&cin,sizeof(cin));

	while(1)
	{
	
		/* 阻塞等待客户端连接 */
		if((newfd = accept(socketfd,(struct sockaddr *)&cin,&cin_add_len)) < 0)
		{
			perror("acept");
			goto _error2;
		}

		/* 如果连接从成功,创建子进程 */
		if((pid = fork()) < 0)
		{
			perror("fork");
			goto _error3;
		}
		else if(pid == 0)//子进程
		{
			close(socketfd);
			do_client_handle(newfd,cin,db);//子进程处理客户端词典请求操作
		}
		else //父进程
		{
			close(newfd);
			waitpid(-1, &status, WNOHANG);//以非阻塞方式回收当前进程的任意一个子进程
		}
	}
_error3:
	close(newfd);
_error2:
	close(socketfd);
_error1:
	sqlite3_close(db);
	return 0;
}

int do_create_table(sqlite3 *db)
{
	char sql[128];
	char **resultp;
	int nrow;
	int ncolumn;
	char *errmsg;

	/* 判断表格usr是否存在,不存在则创建 */
	sprintf(sql,"select * from sqlite_master where type='table' and name='usr';");
	sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg);
	if(nrow == 0)
	{
		sprintf(sql,"create table %s(name text primary key,permission integer,password text);","usr");
		if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
		{
			printf("create table:%s %s\n","usr",errmsg);
			return -1;
		}
		else
		{
			printf("create table:%s success.\n","usr");
		}
	}
	else
	{
		printf("table:%s is OK.\n","usr");
	}

	/* 判断表格record是否存在,不存在则创建 */
	sprintf(sql,"select * from sqlite_master where type='table' and name='record';");
	sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg);
	if(nrow == 0)
	{
		sprintf(sql,"create table %s(name text,date text,word text,is_undefine Integer);","record");
		if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
		{
			printf("create table:%s %s\n","record",errmsg);
			return -1;
		}
		else
		{
			printf("create table:%s success.\n","record");
		}
	}
	else
	{
		printf("table:%s is OK.\n","record");
	}
	return 0;
}

int do_client_handle(int newfd,struct sockaddr_in cin,sqlite3 *db)//子进程处理客户端词典请求操作
{
	MSG msg;
	int ret = -1;
	char ipv4_addr[16];

	inet_ntop(AF_INET,(void *)&cin.sin_addr.s_addr,ipv4_addr,sizeof(cin));
	printf("client(ip:%s port:%d) is connected.\n",ipv4_addr,ntohs(cin.sin_port));

	while(1)
	{
		bzero(&msg,sizeof(msg));
		do{
			ret = recv(newfd,&msg,sizeof(msg),0);
		}while(ret < 0 && errno == EINTR);//阻塞读取
		if(ret < 0)
		{
			perror("recv");
			close(newfd);
			return -1;
		}
		if(ret == 0) //客户端已关闭
		{
			goto _exithandle;
			break;
		}

		switch (msg.type)
		{
			case R:
				do_register(newfd,msg,db);
				break;
			case L:
				do_login(newfd,msg,db);
				break;
			case Q:
				do_query(newfd,msg,db);
				break;
			case H:
				do_history(newfd,msg,db);
				break;
			default:
				printf("Invalid data msg.\n");
		}
	}
_exithandle:
	printf("client(ip:%s port:%d) exit.\n",ipv4_addr,ntohs(cin.sin_port));
	close(newfd);
	exit(0);
	return 0;
}

int	do_register(int acceptfd,MSG msg,sqlite3 *db)
{
	char sql[128];
	char *errmsg;

	/* 将用户信息插入数据库表格usr(名字(主键),权限,密码) */
	sprintf(sql,"insert into usr values('%s',%d,'%s');",msg.name,msg.flag,msg.data);

	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
	{
		printf("do_register %s\n",errmsg);
		strcpy(msg.data,"the name already exit.");//插入失败说明用户名已存在
	}
	else
	{
		printf("insert usr success.\n");
		strcpy(msg.data,"OK");
	}

	if(send(acceptfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("do_register fail to send");
		return -1;
	}
	return 0;
}
int do_login(int acceptfd,MSG msg,sqlite3 *db)
{
	char sql[128];
	char *errmsg;
	char **resultp;
	int nrow;
	int ncolumn;

	sprintf(sql,"select * from  usr where name='%s';",msg.name);

	if(sqlite3_get_table(db,sql,&resultp,&nrow,&ncolumn,&errmsg) < 0)
	{
		printf("do_register %s",errmsg);
		return -1;
	}

	if(nrow == 0)
	{
		printf("login name:%s does not exit.\n",msg.name);
		strcpy(msg.data,"the name does not exit.");
	}
	else if(nrow == 1)//用户存在
	{
		if(msg.flag != atoi(resultp[ncolumn+1]))
		{
			printf("login usr permissions not match.\n");
			strcpy(msg.data,"usr permission not match.");
		}
		else if(msg.flag == atoi(resultp[ncolumn+1]))//用户和权限匹配
		{
			if(strncmp(msg.data,resultp[ncolumn+2],strlen(msg.data)) != 0)
			{
				printf("login password is wrony.\n");
				strcpy(msg.data,"password is wrong.");
			}
			else//登录成功
			{
				printf("login success.\n");
				strcpy(msg.data,"OK");
			}
		}
	}
	if(send(acceptfd,&msg,sizeof(msg),0) < 0)
	{
		perror("do_login fail to send");
		return -1;
	}


	return 0;
}
int	do_query(int acceptfd,MSG msg,sqlite3 *db)
{
	char date[20];
	int found;
	char word[20];//保存要查询的单词
	int is_undefine;
	char sql[128];
	char *errmsg;

	/* 获取本地时间 */
	get_date(date);

	/* 查询词典数据库文件dict.txt */
	strcpy(word,msg.data);
	found = (do_searchworld(&msg));

	if(found == 1)
	{
		is_undefine = 0;
	}
	if(found == 0)
	{
		is_undefine = 1;
	}

	/* 将查询信息发送给客户端:释义|The meaning of the word could not be found|错误信息 */
	if(send(acceptfd,&msg,sizeof(MSG),0) < 0)
	{
		perror("do_query fail to send.");
		return -1;
	}

	/* 一个单词查询完毕,服务端打印提示信息 */
	printf("Word:%s query finished.\n",word);


	/* 将查询信息插入数据库表格record(名字,日期时间,单词,是否未定义) */
	sprintf(sql,"insert into record values('%s','%s','%s',%d);",msg.name,date,word,is_undefine);

	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) != SQLITE_OK)
	{
		printf("do_query %s\n",errmsg);
		return -1;
	}

	return 0;
}

int	do_history(int acceptfd,MSG msg,sqlite3 *db)
{
	HIS_MSG his_msg;
	char sql[128];
	char *errmsg;

	/* 装填回调函数传参结构体 */
	his_msg.fd = acceptfd;
	his_msg.msg = msg;

	/* 装填sql语句 */
	if(msg.flag == USR)
	{
		sprintf(sql,"select * from record where name='%s' and is_undefine=%d;",msg.name,0);
	}
	if(msg.flag == ROOT)
	{
		sprintf(sql,"select * from record;");
	}

	/* 查询历史记录,执行回调函数 */
	if(sqlite3_exec(db,sql,history_callback,(void *)&his_msg,&errmsg) != SQLITE_OK)
	{
		printf("do_history %s\n",errmsg);
		return -1;
	}
	else
	{
		printf("Query history record successful.\n");
		/* 历史记录查询完了,给客户端发送查询完毕标志“\0” */
		strcpy(msg.data,"\0");
		if(send(acceptfd,&msg,sizeof(MSG),0) < 0)
		{
			perror("do_history fail to send");
			return -1;
		}
		return 1;
	}
	return 0;
}

const char *get_date(char * date)//获取本地时间 
{
	time_t t;
	struct tm tp;

	time(&t);

	tp = *localtime(&t);

	sprintf(date,"%04d-%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 date;
}

int do_searchworld(MSG *msg)//查询词典数据库文件dict.txt中的单词释义
{
	FILE *fp;
	int len;
	char str[512];
	int result;
	char *p;

	/* 打印要查询的单词信息 */
	len = strlen(msg->data);
	printf("%s , %d\n",msg->data,len);

	if((fp = fopen("dict.txt","r")) == NULL)
	{
		perror("do_searchworld fail to fopen");
		strcpy(msg->data,"can't open online dictionary database dict.txt.\n");
		return -1;
	}

	/* 从流中读取一行 */
	while(fgets(str,511,fp) != NULL)
	{
		result = strncmp(str,msg->data,len);

		if(result < 0)
		{
			continue ;
		}
		if(result > 0 || str[len] != ' ')
		{
			strcpy(msg->data,"The meaning of the word could not be found.\n");
			break;
		}

		/* 找到了查询的单词 */
		p = str +len;
		while(*p == ' ')//去掉单词释义前的空格
		{
			p++;
		}
		strcpy(msg->data,p);
		fclose(fp);
		return 1;
	}

	fclose(fp);
	return 0;
}

int history_callback(void *his_msg, int f_num, char **f_value, char **f_name)
{
	HIS_MSG *h_msg = (HIS_MSG *)his_msg;
	/*create table %s(name text,date text,word text,is_undefine Integer);","record");*/
	if(h_msg->msg.flag == USR)
	{
		sprintf(h_msg->msg.data,"%s    %s",f_value[1],f_value[2]);
		printf("%s\n",h_msg->msg.data);
	}
	if(h_msg->msg.flag == ROOT)
	{
		sprintf(h_msg->msg.data,"%-20s%s    %-15s%s",f_value[0],f_value[1],f_value[2],f_value[3]);
	}

	if(send(h_msg->fd,&h_msg->msg,sizeof(MSG),0) < 0)
	{
		perror("history_callback fail to send");
		return -1;
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_37542524/article/details/85001614