Linux在线词典项目,实现注册,登陆,查询单词以及查询历史记录,退出查询等功能,(涉及网络编程,进程线程,文件io,sqlite)

服务器:

head.h

#ifndef _HEAD_H_
#define _HEAD_H_

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


//消息的类型
#define USER_REGISTER    10
#define USER_LOGIN       20
#define USER_WORD        30
#define USER_SUCCESS     40
#define USER_FAILURE     50

//__attribute__((__packed__))
//作用:告诉编译器在编译的过程中取消优化对齐。
//      方便我们在发送和接收数据的时候一个字节一个字节的排列 
typedef struct
{
	char _username[25]; //用户名
	char _password[25]; //密码
}__attribute__((__packed__))user_t;

typedef struct
{
	int type;           //消息类型
	int size;           //消息大小
	union
	{
		user_t uinfo;	    //用户信息
		char   _word[1024]; 
	}content;

//客户端填单词,服务端填写单词解释
#define word         content._word
#define username     content.uinfo._username
#define password       content.uinfo._password
}__attribute__((__packed__))mhead_t;

//'\'表示多行链接上一行表示, #deifne ....do...while(0);
//表示封装成独立的语法单元,防止被语法错误。
//注意:'\'之后不要留空格,要不然编译会有警告
extern int do_client(int sockfd,sqlite3 *pdb);

#define EXEC_SQL(db,sql,errmsg) do{\
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) < 0)\
	{\
		fprintf(stderr,"sqlite3 execl [%s] error : %s.\n",sql,errmsg);\
		exit(EXIT_FAILURE);\
	}\
}while(0);

#endif

server.c

#include "head.h"

void signal_handler(int signum)//
{
	waitpid(-1,NULL,WNOHANG);
	return;
}
int init_tcp(char *ip,char *port)//建立套接字,绑定ip和端口号
{
	int sockfd;
	struct sockaddr_in server_addr;
	
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("Fail to socket");
		exit(EXIT_FAILURE);
	}
	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port   = htons(atoi(port));
	server_addr.sin_addr.s_addr = inet_addr(ip);

	if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
	{
		perror("Fail to bind");
		exit(EXIT_FAILURE);
	}
	
	listen(sockfd,5);
	printf("listen....\n");
	return sockfd;
}

//.server ip port db
//数据库中已经手动创建了2个表:user_table,word_table
//注:由于我们后面函数要传承,故这里的const应该去掉
int main(int argc, char *argv[])
{
	int pid;	
	sqlite3 *pdb;
	int listenfd,connect_fd;
	int addr_len = sizeof(struct sockaddr);
	struct sockaddr_in peer_addr;

	if(argc < 4)
	{
		fprintf(stderr,"Usage : %s ip port system.db.\n",argv[0]);
		exit(EXIT_FAILURE);
	}

	//探测子进程的改变状态,回收僵尸态子进程
	if(signal(SIGCHLD,signal_handler) == SIG_ERR)
	{
		perror("Fail to signal");
		exit(EXIT_FAILURE);
	}

	if(sqlite3_open(argv[3],&pdb) != SQLITE_OK)
	{
		fprintf(stderr,"sqlite3 open %s : %s.\n",argv[3],sqlite3_errmsg(pdb));
		exit(EXIT_FAILURE);
	}

	//初始化tcp连接,得到监听套接字
	listenfd = init_tcp(argv[1],argv[2]);

	//提取客户段的链接请求,创建子进程和客户端交互
	while(1)
	{
		if((connect_fd = accept(listenfd,(struct sockaddr *)&peer_addr,&addr_len)) < 0)
		{
			perror("Fail to accept");
			exit(EXIT_FAILURE);
		}
		
		if((pid = fork()) < 0)
		{
			perror("Fail to fork");
			exit(EXIT_FAILURE);
		}

		//创建子进程处理客户端的请求
		if(pid == 0){
			close(listenfd);
			do_client(connect_fd,pdb);
		}

		close(connect_fd);
	}

	exit(EXIT_SUCCESS);
}



 

do_client.c

#include "head.h" 

int do_register(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{
	char *errmsg;
	char buf[1024];
	char **dbresult;
	int nrow = 0,ncolumn = 0;
	char sql[1024] = {0};
	mhead_t *head = (mhead_t *)buf;	

	sprintf(sql,"select * from user_table where NAME='%s';",_username);
	if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0)
	{
		fprintf(stderr,"sqlite3 get table error : %s.\n",errmsg);
		exit(EXIT_FAILURE);
	}

	//没有这样的用户名
	if(nrow == 0)
	{
		//录入数据库
		bzero(sql,sizeof(sql));
		sprintf(sql,"insert into user_table values('%s','%s');",_username,_password);
		EXEC_SQL(pdb,sql,errmsg);

		printf("ok ........\n");

		head->type = USER_SUCCESS;
		if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
		//注册失败,用户名存在
	}else{
		head->type = USER_FAILURE;
		if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
		//表示未知	
		printf("???????\n");
	}
	//插入到数据库之后,释放dbresult结果
	sqlite3_free_table(dbresult);
	return 0;
}

int do_login(int sockfd,sqlite3 *pdb,char *_username,char *_password)
{
	char *errmsg;
	char buf[1024];
	char **dbresult;
	char sql[1024] ={0};
	int nrow = 0,ncolumn= 0;
	mhead_t *head = (mhead_t *)buf;

	sprintf(sql,"select * from user_table where NAME = '%s';",_username);
	if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0)
	{
		fprintf(stderr,"sqlite3 get table error404 : %s.\n",errmsg);
		exit(EXIT_FAILURE);
	}
	//	用户名存在
	if(nrow == 1)
	{
		bzero(sql,sizeof(sql));
		sprintf(sql,"select * from user_table where PASSWORD = '%s';",_password);//确认用户名和密码是否匹配

		if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != 0)
		{
			perror("sqlite3_get_table");
			exit(EXIT_FAILURE);
		}else
		{
			head->type = USER_SUCCESS;
			if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
			{
				perror("Fail to send");
				exit(EXIT_FAILURE);
			}
			printf("..........ok\n");
		}
	}
	else{
		head->type = USER_FAILURE;
		if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
		printf("??????\n");
	}

	sqlite3_free_table(dbresult);//插入到数据库之后,释放dbresult结果
	return 0;
}

int do_query(int sockfd,sqlite3 *pdb,char *_word)
{
	int i,j;
	char *errmsg;
	int count = 0;
	char buf[1024];//定义历史记录缓存区
	char sql[1024] = {0};
	char **dbresult;
	int nrow = 0, ncolumn = 0;
	mhead_t *head = (mhead_t *)buf;

	

	sprintf(sql,"select * from  dict_table where word ='%s';",_word);
	if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg)!=0)
	{
		fprintf(stderr,"sqlite3 get table error :%s.\n",errmsg);
		exit(EXIT_FAILURE);
	}

	if(nrow == 1)
	{
	  bzero(sql,sizeof(sql));
	  sprintf(sql,"select * from dict_table;");
	  if(sqlite3_get_table(pdb,sql,&dbresult,&nrow,&ncolumn,&errmsg) != SQLITE_OK)
	  {
		fprintf(stderr,"sqlite3_get_table %s\n",errmsg);
		exit(EXIT_FAILURE);
	  }
	  
	  
	  for(i = 0;i <= nrow;i++)
	  {
		for(j = 0;j < ncolumn;j++)
		{
			if(strcmp(dbresult[count],_word) == 0)
			{
		     	strcpy(head->word,dbresult[count]);
			    strcat(head->word, "   ");
                strcat(head->word,dbresult[count+1]);
				printf("%s\n",dbresult[count + 1]);
			    i = nrow +1;
				break;
			 }
			count ++;
	     }
    
      }
	  head->type = USER_SUCCESS;
	  if(send(sockfd,buf,sizeof(mhead_t),0)<0)
	  {
		 perror("Fail to send");
	     bzero(sql,sizeof(sql));
		 exit(EXIT_FAILURE);
	  }
   }
   	else
	{
		head->type = USER_FAILURE;
		if(send(sockfd,buf,sizeof(mhead_t),0)<0 )
		{
			perror("Fail to send");
           exit(EXIT_FAILURE);
		}
        printf("?????\n");
	}
	sqlite3_free_table(dbresult);
}
int do_client(int sockfd,sqlite3 *pdb)
{
	int n;
	int count = 0;
	char buf[1024];
	mhead_t *head = (mhead_t *)buf;	

	while(1)
	{
		count = 0;
		//接收协议头
		while(1)
		{
			n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
			if(n <= 0){
				exit(EXIT_FAILURE);
			}

			count += n;
			printf("count : %d mhead_t : %ld\n",count,sizeof(mhead_t));
			if(count == sizeof(mhead_t))
				break;
		}

		switch(head->type)
		{
		case USER_REGISTER:
			do_register(sockfd,pdb,head->username,head->password);	
			break;

		case USER_LOGIN:
			do_login(sockfd,pdb,head->username,head->password);
			break;
		case USER_WORD:
			do_query(sockfd,pdb,head->word);
			break;
defalut:
			exit(EXIT_SUCCESS);
		}	
	}

	return 0;
}

客户端

head.h

#ifndef _HEAD_H_
#define _HEAD_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sqlite3.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//消息的类型
#define USER_REGISTER    10
#define USER_LOGIN       20
#define USER_WORD        30
#define USER_SUCCESS     40
#define USER_FAILURE     50

//__attribute__((__packed__))
//作用:告诉编译器在编译的过程中取消优化对齐。
//      方便我们在发送和接收数据的时候一个字节一个字节的排列 
typedef struct
{
	char _username[25]; //用户名
	char _password[25]; //密码
}__attribute__((__packed__))user_t;

typedef struct
{
	int type;           //消息类型
	int size;           //消息大小
	union
	{
		user_t uinfo;	    //用户信息
		char   _word[1024]; 
	}content;

//客户端填单词,服务端填写单词解释
#define word         content._word
#define username     content.uinfo._username
#define password       content.uinfo._password
}__attribute__((__packed__))mhead_t;


extern void help_info2();
extern int do_task(int sockfd); 
extern int do_query(int sockfd);
extern int do_task2(int sockfd);
//'\'表示多行链接上一行表示, #deifne ....do...while(0);
//表示封装成独立的语法单元,防止被语法错误。
//注意:'\'之后不要留空格,要不然编译会有警告

#define EXEC_SQL(db,sql,errmsg) do{\
	if(sqlite3_exec(db,sql,NULL,NULL,&errmsg) < 0)\
	{\
		fprintf(stderr,"sqlite3 execl [%s] error : %s.\n",sql,errmsg);\
		exit(EXIT_FAILURE);\
	}\
}while(0);

#endif

client.c

#include "head.h"
//用户提示界面1
void help_info1()
{
	printf("\t-----------------------------------------------\n");
	printf("\t|               HENRY   在线辞典               |\n");
	printf("\t|版本:0.0.1                                    |\n");
	printf("\t|作者:HENRY老师                                |\n");
	printf("\t|功能:                                         |\n");
	printf("\t|    [1] 登录                                  |\n");
	printf("\t|    [2] 注册                                  |\n");
	printf("\t|    [3] 退出                                  |\n");
	printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");
	printf("\t------------------------------------------------\n");
	return;
}

void help_info2()
{
	printf("\t-----------------------------------------------\n");
	printf("\t|               HENRY   在线辞典               |\n");
	printf("\t|版本:0.0.1                                    |\n");
	printf("\t|作者:HENRY老师                                |\n");
	printf("\t|功能:                                         |\n");
	printf("\t|    [1] 查询单词                              |\n");
	printf("\t|    [2] 查询历史记录                          |\n");
	printf("\t|    [3] 退出查询系统                                  |\n");
	printf("\t|注意:用户只有登录成功后才能进入查单词界面     |\n");
	printf("\t----------------------------------------------- \n");
	return;
		
}


//用户输入指令,供大家选择
enum{
	LOGIN    = 1,  //登陆
	REGISTER = 2,  //注册
	QUIT     = 3,  //退出
	QUERY    = 1,  //查询单词
	HISTORY  = 2,  //查询历史
};

int init_tcp(char *ip,char *port)
{
	int sockfd;
	struct sockaddr_in server_addr;

	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		perror("Fail to socket");	
		exit(EXIT_FAILURE);
	}

	bzero(&server_addr,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(port));
	server_addr.sin_addr.s_addr = inet_addr(ip);

	if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
	{
		perror("Fail to bind");	
		exit(EXIT_FAILURE);
	}

	return sockfd;
}

int do_register(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	//定义发送的协议头
	mhead_t *head = (mhead_t *)buf;

	printf("\n您正在注册,请输入用户名和密码\n");

	head->type = USER_REGISTER;
	head->size = sizeof(mhead_t);

	printf("Input username : ");
	fgets(head->username,sizeof(head->username),stdin);
	head->username[strlen(head->username) - 1] = '\0';

	printf("Input password : ");
	fgets(head->password,sizeof(head->password),stdin);
	head->password[strlen(head->password) - 1] = '\0';

	//发给服务器端
	
	if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}
	
	bzero(&buf,sizeof(buf));
	while(1)
	{
		//接收数据,TCP是可靠的连接,若是数据
		//未完全接收的话,可以在接收
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);

		if(n <= 0){
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
		//若是数据未发送完成,再次接收的时候可补充
		count += n;
		if(count == sizeof(mhead_t))
			break;
	}

	if(head->type == USER_SUCCESS)
	{
		printf("\n恭喜您,注册成功!\n");	
		return 0;
	}else{
		printf("\n很遗憾,这个用户名已经被其它用户注册过了,请重新注册");	
		return -1;
	}

}

int do_login(int sockfd)
{
	int n = 0;
	int count = 0;
	char buf[1024] = {0};
	mhead_t *head = (mhead_t *)buf;//定义发送的协议头

	printf("\n您正在登陆,请输入用户名和密码\n");
	
	head->type = USER_LOGIN;
	head->size = sizeof(mhead_t);

	printf("Input username : ");
	fgets(head->username,sizeof(head->username),stdin);
	head->username[strlen(head->username) - 1] = '\0';

	printf("Input password : ");
    fgets(head->password,sizeof(head->password),stdin);
	head->password[strlen(head->password) - 1] = '\0';

    if(send(sockfd,buf,sizeof(mhead_t),0) < 0)
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
    }	 
	bzero(&buf,sizeof(buf));
	while(1)
	{
		n = recv(sockfd,buf + count,sizeof(mhead_t) - count,0);
		if(n <= 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
	
	count += n;
	if(count == sizeof(mhead_t))
		break;
	}
	if(head->type == USER_SUCCESS)
	{
		printf("\n恭喜您,登陆成功\n");
		do_task2(sockfd);
		return 0;
	}else
	{
		printf("\n抱歉,用户名或密码错误");
		return -1;
	}
}  

int do_query(int sockfd)//单词查询
{
	int fd;
	int ret = 0;
	time_t t;
	time(&t);
	char buf[1024];
	char words[1024];
	int count = 0;
	ssize_t wr_bytes;
	mhead_t *head = (mhead_t *)buf;

	printf("\n您,正在查询单词!\n");
	head->type = USER_WORD;
	head->size = sizeof(mhead_t);

	printf("Input word : ");
	fgets(head->word,sizeof(head->word),stdin);
 	head->word[strlen(head->word)-1] ='\0';

	if(send(sockfd,buf,sizeof(mhead_t),0)<0) //发送给服务器
	{
		perror("Fail to send");
		exit(EXIT_FAILURE);
	}
	bzero(buf,sizeof(buf));
	while(1)
	{
		ret = recv(sockfd,buf+count,sizeof(mhead_t)-count,0);	
		if(ret <= 0)
		{
			perror("Fail to send");
			exit(EXIT_FAILURE);
		}
		count += ret;
		if(ret == sizeof(mhead_t))
        {
			break;
		}
	}
    if(head->type == USER_SUCCESS) //单词存在 
	{
		printf("%s\n",head->word);
		fd = open("word_record.txt",O_RDWR|O_CREAT|O_APPEND,0666); //打开文件
		if(fd == -1)
		{
			perror("open");
			return -1;
		}
		sprintf(words,"%s %s\n",ctime(&t),head->word);//单词以及查询时间放入数据库
		wr_bytes = write(fd,words,strlen(words)); //单词写入文件

		if(wr_bytes == -1)
		{
			perror("write");
			return -1;
		}
		do_task2(sockfd);
	}
	else
	{
		printf("no such word\n");//该单词不存在
		return -1;
	}
	close(fd);
}

int do_history(int sockfd)//查询历史记录
{
	FILE *fp;
	char words[1024];
	char * rd_bytes;
	fp = fopen("word_record.txt","r");//以只读方式打开文件
	if(fp == NULL)
	{
		perror("");
		return -1;
	}
	fseek(fp,0,SEEK_SET);
	while(1)
	{
       rd_bytes = fgets(words,sizeof(words),fp); //按行写入
		if(rd_bytes == NULL) //没有该单词
		{
			perror("read");
			return -1;
		}
		else if(rd_bytes == 0)//有该单词
		{
			break;//找到单词,跳出循环
		}
 		else
		{
			printf("%s\n",words);
		}

	}
	do_task2;
	fclose(fp);
}




int do_task(int sockfd)
{
	int cmd;
	while(1)
	{
		//提示界面帮助,用户选择
		help_info1();	

		printf("\n\n请选择>");
		scanf("%d",&cmd);
		//吃掉回车键
		getchar();
		switch(cmd)
		{
			//用户注册,我们先来写注册的函数
			case REGISTER:
				if(do_register(sockfd) < 0)
					continue;
					break;
			//用户登陆
			case LOGIN:	
					if(do_login(sockfd) < 0)
						continue;
					 break;
			case QUIT:
				exit(EXIT_SUCCESS);
			default:
				printf("Unknow cmd.\n");
				continue;
		}
	}
	return 0;
}

int do_task2(int sockfd)
{
	int cmd;
	while(1)
	{
		//登陆成功选项页面
		help_info2();
		printf("\n\n请选择>");
		scanf("%d",&cmd);
		//吃掉回车键
		getchar();
		switch(cmd)
		{
		case QUERY://用户查询单词
			if(do_query(sockfd) < 0)
			continue;
			break;
		case HISTORY://历史查询
			if(do_history(sockfd < 0))
			continue;
			break;
		case QUIT:
				exit(EXIT_SUCCESS);
		default:
				printf("Unknow cmd.\n");
				continue;

		}

	    
	}
}




//./client ip port 
//由于后面要传递参数,故这里的const省略
int main(int argc, char *argv[])
{
	int sockfd;	
	int addr_len = sizeof(struct sockaddr);
	struct sockaddr_in peer_addr;

	if(argc < 3)
	{
		fprintf(stderr,"Usage : %s argv[1] argv[2]\n",argv[0]);	
		exit(EXIT_FAILURE);
	}

	sockfd = init_tcp(argv[1],argv[2]);

	do_task(sockfd);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值