Linux小项目:在线词典开发

在线词典介绍

  • 流程图如下:
    在这里插入图片描述
  • 项目的功能介绍
    • 在线英英词典
    • 项目功能描述
    • 用户注册和登录验证
    • 服务器端将用户信息和历史记录保存在数据中。客户端输入用户和密码,服务器端在数据库中查找、匹配,返回结果
    • 单词在线翻译
    • 根据客户端输入输入的单词在字典文件中搜索
    • 历史记录查询
    • 项目分析
  • 项目流程
    • 定义数据库中表的结构
    • 定义消息结构体
    • 分析服务端和客户端流程
    • 编码实现
  • 客户端流程图分析
    • 服务器和客户端通信的结构体
      在这里插入图片描述
    • 客户端流程图如下:
      在这里插入图片描述
    • 服务器端流程图如下: ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/64b2312c006e4298a3f382597ff62558.png

在线词典演示

  • 分别打开两个终端然后运行客户端和服务端如下:
    在这里插入图片描述
  • 然后用客户端注册一个用户
    在这里插入图片描述
  • 在客户端登录用户
    在这里插入图片描述
  • 执行查询功能如下:
    在这里插入图片描述
  • 客户端执行历史查询功能如下:
    在这里插入图片描述

在线词典的代码编写

  • 客户端代码的编写:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    
    #define   N  32
    
    #define  R  1   // user - register
    #define  L  2   // user - login
    #define  Q  3   // user - query
    #define  H  4   // user - history
    
    // 定义通信双方的信息结构体
    typedef struct {
    	int type;
    	char name[N];
    	char data[256];
    }MSG;
    
    
    int  do_register(int sockfd, MSG *msg)
    {
    	msg->type = R;
    
    	printf("Input name:");
    	scanf("%s", msg->name);
    	getchar();
    
    	printf("Input passwd:");
    	scanf("%s", msg->data);
    
    	if(send(sockfd, msg, sizeof(MSG),0) < 0)
    	{
    		printf("fail to send.\n");
    		return -1;
    	}
    
    	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    	{
    		printf("Fail to recv.\n");
    		return -1;
    	}
    
    	// ok !  or  usr alread exist.
    	printf("%s\n", msg->data);
    
    	return 0;
    }
    
    int do_login(int sockfd, MSG *msg)
    {
    	msg->type = L;
    
    	printf("Input name:");
    	scanf("%s", msg->name);
    	getchar();
    
    	printf("Input passwd:");
    	scanf("%s", msg->data);
    
    	if(send(sockfd, msg, sizeof(MSG),0) < 0)
    	{
    		printf("fail to send.\n");
    		return -1;
    	}
    
    	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    	{
    		printf("Fail to recv.\n");
    		return -1;
    	}
    
    	if(strncmp(msg->data, "OK", 3) == 0)
    	{
    		printf("Login ok!\n");
    		return 1;
    	}
    	else 
    	{
    		printf("%s\n", msg->data);
    	}
    
    	return 0;
    }
    
    int do_query(int sockfd, MSG *msg)
    {
    	msg->type = Q;
    	puts("--------------");
    
    	while(1)
    	{
    		printf("Input word:");
    		scanf("%s", msg->data);
    		getchar();
    
    		//客户端,输入#号,返回到上一级菜单
    		if(strncmp(msg->data, "#", 1) == 0)
    			break;
    
    		//将要查询的单词发送给服务器
    		if(send(sockfd,msg, sizeof(MSG), 0) < 0)
    		{
    			printf("Fail to send.\n");
    			return -1;
    		}
    
    		// 等待接受服务器,传递回来的单词的注释信息
    		if(recv(sockfd, msg,sizeof(MSG), 0) < 0)
    		{
    			printf("Fail to recv.\n");
    			return -1;
    		}
    		printf("%s\n", msg->data);
    	}
    		
    	return 0;
    }
    
    int do_history(int sockfd, MSG *msg)
    {
    
    	msg->type = H;
    
    	send(sockfd, msg, sizeof(MSG), 0);
    	
    	// 接受服务器,传递回来的历史记录信息
    	while(1)
    	{
    		recv(sockfd, msg, sizeof(MSG), 0);
    
    		if(msg->data[0] == '\0')
    			break;
    
    		//输出历史记录信息
    		printf("%s\n", msg->data);
    	}
    
    	return 0;
    }
    
    // ./server  192.168.3.196  10000
    int main(int argc, const char *argv[])
    {
    
    	int sockfd;
    	struct sockaddr_in  serveraddr;
    	int n;
    	MSG  msg;
    
    	if(argc != 3)
    	{
    		printf("Usage:%s serverip  port.\n", argv[0]);
    		return -1;
    	}
    
    	if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    	{
    		perror("fail to socket.\n");
    		return -1;
    	}
    
    	bzero(&serveraddr, sizeof(serveraddr));
    	serveraddr.sin_family = AF_INET;
    	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    	serveraddr.sin_port = htons(atoi(argv[2]));
    
    	if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    	{
    		perror("fail to connect");
    		return -1;
    	}
    
    	while(1)
    	{
    		printf("*****************************************************************\n");
    		printf("* 1.register          2.login              3.quit               *\n");
    		printf("*****************************************************************\n");
    		printf("Please choose:");
    
    		scanf("%d", &n);
    		getchar();
    
    		switch(n)
    		{
    		case 1:
    			do_register(sockfd, &msg);
    			break;
    		case 2:
    			if(do_login(sockfd, &msg) == 1)
    			{
    				goto next;
    			}
    			break;
    		case 3:
    			close(sockfd);
    			exit(0);
    			break;
    		default:
    			printf("Invalid data cmd.\n");
    		}
    
    	}
    
    next:
    	while(1)
    	{
    		printf("*****************************************************\n");
    		printf("* 1.query_word   2.history_record   3.quit          *\n");
    		printf("*****************************************************\n");
    		printf("Please choose:");
    		scanf("%d", &n);
    		getchar();
    
    		switch(n)
    		{
    			case 1:
    				do_query(sockfd, &msg);
    				break;
    			case 2:
    				do_history(sockfd, &msg);
    				break;
    			case 3:
    				close(sockfd);
    				exit(0);
    				break;
    			default :
    				printf("Invalid data cmd.\n");
    		}
    
    	}
    	
    	return 0;
    

}
```

  • 服务器端代码编写如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <unistd.h>
    #include <sqlite3.h>
    #include <signal.h>
    #include <time.h>
    
    
    #define   N  32
    
    #define  R  1   // user - register
    #define  L  2   // user - login
    #define  Q  3   // user - query
    #define  H  4   // user - history
    
    #define  DATABASE  "my.db"
    
    // 定义通信双方的信息结构体
    typedef struct {
    	int type;
    	char name[N];
    	char data[256];
    }MSG;
    
    
    
    int do_client(int acceptfd, sqlite3 *db);
    void 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);
    int history_callback(void* arg,int f_num,char** f_value,char** f_name);
    int do_searchword(int acceptfd, MSG *msg, char word[]);
    int get_date(char *date);
    
    // ./server  192.168.3.196  10000
    int main(int argc, const char *argv[])
    {
    
    	int sockfd;
    	struct sockaddr_in  serveraddr;
    	int n;
    	MSG  msg;
    	sqlite3 *db;
    	int acceptfd;
    	pid_t pid;
    
    	if(argc != 3)
    	{
    		printf("Usage:%s serverip  port.\n", argv[0]);
    		return -1;
    	}
    
    	//打开数据库
    	if(sqlite3_open(DATABASE, &db) != SQLITE_OK)
    	{
    		printf("%s\n", sqlite3_errmsg(db));
    		return -1;
    	}
    	else
    	{
    		printf("open DATABASE success.\n");
    	}
    
    	if((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0)
    	{
    		perror("fail to socket.\n");
    		return -1;
    	}
    
    	bzero(&serveraddr, sizeof(serveraddr));
    	serveraddr.sin_family = AF_INET;
    	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    	serveraddr.sin_port = htons(atoi(argv[2]));
    
    	if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    	{
    		perror("fail to bind.\n");
    		return -1;
    	}
    
    	// 将套接字设为监听模式
    	if(listen(sockfd, 5) < 0)
    	{
    		printf("fail to listen.\n");
    		return -1;
    	}
    
    	//处理僵尸进程
    	signal(SIGCHLD, SIG_IGN);
    
    	while(1)
    	{
    		if((acceptfd = accept(sockfd, NULL, NULL)) < 0)
    		{
    			perror("fail to accept");
    			return -1;
    		}
    
    		if((pid = fork()) < 0)
    		{
    			perror("fail to fork");
    			return -1;
    		}
    		else if(pid == 0)  // 儿子进程
    		{
    			//处理客户端具体的消息
    			close(sockfd);
    			do_client(acceptfd, db);
    
    		}
    		else  // 父亲进程,用来接受客户端的请求的
    		{
    			close(acceptfd);
    		}
    	}
    	
    	return 0;
    }
    
    
    int do_client(int acceptfd, sqlite3 *db)
    {
    	MSG msg;
    	while(recv(acceptfd, &msg, sizeof(msg), 0) > 0)
    	{
    	  printf("type:%d\n", msg.type);
    	   switch(msg.type)
    	   {
    	  	 case R:
    			 do_register(acceptfd, &msg, db);
    			 break;
    		 case L:
    			 do_login(acceptfd, &msg, db);
    			 break;
    		 case Q:
    			 do_query(acceptfd, &msg, db);
    			 break;
    		 case H:
    			 do_history(acceptfd, &msg, db);
    			 break;
    		 default:
    			 printf("Invalid data msg.\n");
    	   }
    
    	}
    
    	printf("client exit.\n");
    	close(acceptfd);
    	exit(0);
    
    	return 0;
    }
    
    void do_register(int acceptfd, MSG *msg, sqlite3 *db)
    {
    	char * errmsg;
    	char sql[128];
    
    	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
    	printf("%s\n", sql);
    
    	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    		strcpy(msg->data, "usr name already exist.");
    	}
    	else
    	{
    		printf("client  register ok!\n");
    		strcpy(msg->data, "OK!");
    	}
    
    	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
    	{
    		perror("fail to send");
    		return ;
    	}
    
    	return ;
    }
    
    int do_login(int acceptfd, MSG *msg , sqlite3 *db)
    {
    	char sql[128] = {};
    	char *errmsg;
    	int nrow;
    	int ncloumn;
    	char **resultp;
    
    	sprintf(sql, "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data);
    	printf("%s\n", sql);
    
    	if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg)!= SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    		return -1;
    	}
    	else
    	{
    		printf("get_table ok!\n");
    	}
    
    	// 查询成功,数据库中拥有此用户
    	if(nrow == 1)
    	{
    		strcpy(msg->data, "OK");
    		send(acceptfd, msg, sizeof(MSG), 0);
    		return 1;
    	}
    
    	if(nrow == 0) // 密码或者用户名错误
    	{
    		strcpy(msg->data,"usr/passwd wrong.");
    		send(acceptfd, msg, sizeof(MSG), 0);}
    
    	return 0;
    }
    
    int do_searchword(int acceptfd, MSG *msg, char word[])
    {
    	FILE * fp;
    	int len = 0;
    	char temp[512] = {};
    	int result;
    	char *p;
    
    
    	//打开文件,读取文件,进行比对
    	
    	if((fp = fopen("dict.txt", "r")) == NULL)
    	{
    		perror("fail to fopen.\n");
    		strcpy(msg->data, "Failed to open dict.txt");
    		send(acceptfd, msg, sizeof(MSG), 0);
    		return -1;
    	}
    
    	//打印出,客户端要查询的单词
    	len = strlen(word);
    	printf("%s , len = %d\n", word, len);
    
    	//读文件,来查询单词
    	while(fgets(temp, 512, fp) != NULL)
    	{
    
    	//	printf("temp:%s\n", temp);
    
    		// abandon  ab
    		result = strncmp(temp,word,len);
    
    		if(result < 0)
    		{
    			continue;
    		}
    		if(result > 0 || ((result == 0) && (temp[len]!=' ')))
    		{
    			break;
    		}
    
    		// 表示找到了,查询的单词
    		p = temp + len; //  abandon   v.akdsf dafsjkj 
    	//	printf("found word:%s\n", p);
    		while(*p == ' ')
    		{
    			p++;
    		}
    
    		// 找到了注释,跳跃过所有的空格
    		strcpy(msg->data, p);
    		printf("found word:%s\n", msg->data);
    
    		// 注释拷贝完毕之后,应该关闭文件
    		fclose(fp);
    		return 1;
    	}
    
    	fclose(fp);
    
    	return 0;
    }
    
    int get_date(char *date)
    {
    	time_t t;
    	struct tm *tp;
    
    	time(&t);
    
    	//进行时间格式转换
    	tp = localtime(&t);
    
    	sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon+1, tp->tm_mday, 
    			tp->tm_hour, tp->tm_min , tp->tm_sec);
    	printf("get date:%s\n", date);
    
    	return 0;
    }
    
    int do_query(int acceptfd, MSG *msg , sqlite3 *db)
    {
    	char word[64];
    	int found = 0;
    	char date[128] = {};
    	char sql[128] = {};
    	char *errmsg;
    
    	//拿出msg结构体中,要查询的单词
    	strcpy(word, msg->data);
    
    	found = do_searchword(acceptfd, msg, word);
    	printf("查询一个单词完毕.\n");
    
    	// 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。
    	if(found == 1)
    	{
    		// 需要获取系统时间
    		get_date(date);
    
            sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);
    
    		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    		{
    			printf("%s\n", errmsg);
    			return -1;
    		}
    		else
    		{
    			printf("Insert record done.\n");
    		}
    
    	}
    	else  //表示没有找到
    	{
    		strcpy(msg->data, "Not found!");
    	}
    
    	// 将查询的结果,发送给客户端
    	send(acceptfd, msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    // 得到查询结果,并且需要将历史记录发送给客户端
    int history_callback(void* arg,int f_num,char** f_value,char** f_name)
    {
    	// record  , name  , date  , word 
    	int acceptfd;
    	MSG msg;
    
    	acceptfd = *((int *)arg);
    
    	sprintf(msg.data, "%s , %s", f_value[1], f_value[2]);
    
    	send(acceptfd, &msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    
    int do_history(int acceptfd, MSG *msg, sqlite3 *db)
    {
    	char sql[128] = {};
    	char *errmsg;
    
    	sprintf(sql, "select * from record where name = '%s'", msg->name);
    
    	//查询数据库
    	if(sqlite3_exec(db, sql, history_callback,(void *)&acceptfd, &errmsg)!= SQLITE_OK)
    	{
    		printf("%s\n", errmsg);
    	}
    	else
    	{
    		printf("Query record done.\n");
    	}
    
    	// 所有的记录查询发送完毕之后,给客户端发出一个结束信息
    	msg->data[0] = '\0';
    
    	send(acceptfd, msg, sizeof(MSG), 0);
    
    	return 0;
    }
    
    
  • 字典文本文件如下:
    链接:https://pan.baidu.com/s/1dTUm-aVnJcXgsYFlyNLfeA?pwd=v3xc
    提取码:v3xc
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在线词典项目描述: 版本号:v1.1 升级描述:1. 同时支持管理员(用户名:root,密码:1)和普通用户 2. 管理员可以查询所有用户的使用记录 服务器: 1. 支持并发服务器,每有一个客户端connect成功后,提示某某客户端已连接并打印客户端的ip和端口号。 2. 服务器程序可在任意IP地址上运行,并且允许IP地址快速重用 3. 接收到客户端的信息后,可以执行相应的操作:注册,登陆,退出 注册:接收到注册新用户指令后,可以创建sqlite3数据库,将用户名和密码存储到数据库的user表中(用户名name为primary key)。 登陆:接收到登陆命令,可以查询客户端输入的用户名和密码数据库中有没有,有的话跳到下一个菜单(查询单词,历史纪录,退出),没有的话打印错误信息。 查询单词:用户输入单词,服务器从dict.txt文件中遍历有无该单词,有的话打印释义,没有的话打印错误信息,并将用(户名,时间,单词)存储到数据库的history_record表中。(‘#’返回上一级菜单) 历史纪录:用户选择历史记录查询,服务器数据库的history_record表查询相同name的记录,每查询到一条,调用一次callback将信息发送到客户端,查询完毕后通知客户端。 退出:客户端退出,服务器打印"client exit!" 退出:客户端退出,服务器打印"client exit!" 客户端: 1. 客户端输入./client 192.168.23.128(服务器IP地址) 10000(端口号),参数格式不对或少报错,端口号不能小于5000,小于5000报错 2. 客户端支持注册,登陆,退出 注册:向服务器发送用户名和密码,接收服务器返回来的信息,注册成功/当前用户已存在 登陆:用户输入用户名和密码,客服端将用户名和密码发送给服务器,接收服务器返回的信息,如果OK,打印Login OK! 进入下一菜单(查询单词,历史纪录,退出),否则打印错误信息 查询单词:用户输入单词,客户端将单词发送给服务器服务器将释义返回给客户端,客户端将释义打印出来 历史纪录:用户选择历史记录查询,客户端将信息发送给服务器服务器循环把该用户的历史查询记录发送给客户端,客户端循环将其打印出来。 退出:客户端关闭套接字后结束进程 退出:客户端关闭套接字后结束进程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值