C语言实现在线词典-TCP通信的方式实现

目录

C语言——在线词典

一、需求分析

 二、客户端功能设计

三、设计数据库mydict.db 

四、引入词典文件-dict.txt

五、代码实现

        1、 客户端:

       2、 服务器端:

六、效果展示


C语言——在线词典

功能介绍:基于Linux操作系统、网络编程和数据库实现在线词典,客户端可以进行注册、登录、查询、查看历史记录和退出功能,服务端基于TCP通信,多线程实现并发访问客户端,并利用sqlite3数据库实现对用户信息的管理。

一、需求分析

1.注册:若用户名已经注册过,可重新注册
2.登录:用户名或密码错误需重新登录
3.查询:输入要查的单词,#键结束查询
4.历史:可以查询当前用户历史查找过的单词
5.退出:退出在线词典

C语言实现在线词典-TCP通信的方式实现_第1张图片

 二、客户端功能设计

C语言实现在线词典-TCP通信的方式实现_第2张图片

三、设计数据库mydict.db 

这里数据库已经设计好了,创建了两个表,一个是user表,用来存放用户名和密码;一个是record表,用来存放查询过的的单词、查询时间和查询用户。

C语言实现在线词典-TCP通信的方式实现_第3张图片

 

四、引入词典文件-dict.txt

部分词典内容展示,这里截图显示。

C语言实现在线词典-TCP通信的方式实现_第4张图片

 C语言实现在线词典-TCP通信的方式实现_第5张图片

 

五、代码实现

        1、 客户端:

//客户端
#include 
#include           
#include 
#include 
#include 
#include 
#include 
#include 

#define N 20
#define M 200
#define R 1 //注册
#define L 2 //登录
#define Q 3 //查询单词
#define H 4 //历史记录
#define E 5 //退出

typedef struct{
	int type;//消息类型
	char name[N];//用户名
	char text[M];//单词 或 密码
}MSG;
#define LEN_SMG sizeof(MSG)
typedef struct sockaddr SA;

//用户注册
void do_register(int sockfd,MSG *pbuf){
	pbuf->type = R;
	printf("用户名:");
	scanf("%s",pbuf->name);
	printf("密码:");
	scanf("%s",pbuf->text);

	send(sockfd,pbuf,LEN_SMG,0);
	recv(sockfd,pbuf,LEN_SMG,0);
	printf("%s\n",pbuf->text);
	sleep(1);

}
//用户登录
int do_login(int sockfd,MSG *pbuf){
	pbuf->type = L;
	printf("用户名:");
	scanf("%s",pbuf->name);
	printf("密码:");
	scanf("%s",pbuf->text);

	send(sockfd,pbuf,LEN_SMG,0);//发送给服务器
	recv(sockfd,pbuf,LEN_SMG,0);//从服务器接收
	if(pbuf->type == 8){
		printf("%s\n",pbuf->text);
		sleep(1);
		return 1;
	}
	else{
		printf("%s\n",pbuf->text);
		return 0;
	}

}
//查询单词
void do_query(int sockfd,MSG *pbuf){
	pbuf->type = Q;
	while(1)
	{
		printf("请输入你要查询的单词(输入#结束):");
		scanf("%s",pbuf->text);
		getchar();
		//客户端输入#返回上一级菜单
		if(strncmp(pbuf->text,"#",1) == 0)
			break;
		//将要查询的单词发送给服务器
		send(sockfd,pbuf,LEN_SMG,0);
		//等待接受服务器,传递回来的单词的注释信息
		recv(sockfd,pbuf,LEN_SMG,0);
		printf("就是这个意思:%s\n",pbuf->text);
	}

}
//查询历史记录
int do_history(int sockfd,MSG *pbuf){
	pbuf->type = H;
	send(sockfd,pbuf,LEN_SMG,0);
	//接受服务器,传递回来的历史信息
	while(1){
		recv(sockfd,pbuf,LEN_SMG,0);
		//历史单词全部接受完成
		if(pbuf->text[0] == '0')
			break;
		//输出历史信息
		printf("%s\n",pbuf->text);
	}
	return 0;
}
//二级菜单
void menu_2(int sockfd,MSG *pbuf){
	while(1){
		printf("*        欢迎使用电子词典              *\n");
		printf("****************************************\n");
		printf("*   1.查询单词 2.查询历史单词 3.退出   *\n");
		printf("****************************************\n");
		printf("请选择:");
		scanf("%d",&pbuf->type);
		switch(pbuf->type){
			case 1:
				do_query(sockfd,pbuf);
				break;
			case 2:
				do_history(sockfd,pbuf);
				break;
			case 3:
				printf("   即将退出,欢迎再次使用!\n");
				send(sockfd,pbuf,LEN_SMG,0);
				sleep(1);
				close(sockfd);
				exit(-1);
				break;
			default:
				printf("错误选项!\n");
				break;
		}
	}
}
//一级菜单
void menu_1(int sockfd,MSG *pbuf){
	
	int num;//选择功能
	while(1){
		printf("*         电子词典项目         *\n");
		printf("********************************\n");
		printf("*   1.注册   2.登录   3.退出   *\n");
		printf("********************************\n");
		printf("请选择:");
		scanf("%d",&num);
		switch(num){
			case 1:
				do_register(sockfd,pbuf);
				break;
			case 2:
				while(do_login(sockfd,pbuf) != 1)
					continue;
				menu_2(sockfd,pbuf);
				break;
			case 3:
				printf("即将退出,欢迎下次使用!\n");
				sleep(1);
				close(sockfd);
				exit(-1);
			default:
				printf("不存在此项操作!\n");
				break;
		}
	}
}

int main(){
	

	int sockfd;
	//创建套接字
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0){
		printf("fail to socket\n");
		return -1;
	}
	socklen_t len = sizeof(SA);
	MSG buf;
	struct sockaddr_in serveraddr;//结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(8888);//端口号
	serveraddr.sin_addr.s_addr = inet_addr("192.168.2.128");//ip地址
	
	//请求服务端建立连接
	connect(sockfd,(SA *)&serveraddr,len);

	//与服务器建立连接后,进入菜单界面
	menu_1(sockfd,&buf);

	return 0;
}

       2、 服务器端:

//服务端
#include 
#include 
#include           /* See NOTES */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define PATH_DICT "./dict.txt" //单词表
#define PATH_DATA "./mydict.db" //数据库
#define N 20
#define SIZE 256
#define R 1 //注册
#define L 2 //登录
#define Q 3 //查询单词
#define H 4 //历史记录
#define E 5 //退出

typedef struct{
	int type;//消息类型
	char name[N];//用户名
	char data[SIZE];//单词 或 密码
}MSG;
#define LEN_SMG sizeof(MSG)

typedef struct sockaddr SA;

//注册功能 R
void do_register(int connfd,MSG *pbuf,sqlite3 *pdb){
	char *errmsg,**result;
	//nrow 查找出的总行数 ncolumn 存储列
	int nrow = 0,ncolumn = 0;
	char sql[1280];
	sprintf(sql,"insert into user values('%s','%s');",pbuf->name,pbuf->data);
	printf("%s\n",sql);
	if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK){
		printf("%s\n",errmsg);
		strcpy(pbuf->data,"user name already exist");
	}
	else{
		printf("client register ok!\n");
		strcpy(pbuf->data,"OK!");
	}
	if(send(connfd,pbuf,LEN_SMG,0) < 0){
		printf("fail to send\n");
		return;
	}
	return;
}
//登录 L
void do_login(int connfd,MSG *pbuf,sqlite3 *pdb){
	char *errmsg,**result;
	char sql[1280]={};
	int nrow = 0,ncloumn = 0;
	sprintf(sql,"select * from user where name = '%s'and pass = '%s';",pbuf->name,pbuf->data);
	printf("%s\n",sql);
	if(sqlite3_get_table(pdb,sql,&result,&nrow,&ncloumn,&errmsg) != SQLITE_OK){
		printf("%s\n",errmsg);
		exit(-1);
	}
	//查询成功,数据库有此用户
	if(nrow > 0)
	{
		sprintf(pbuf->data,"\n   %s恭喜您,登录成功\n",pbuf->name);
		pbuf->type = 8;
	}
	//密码或者用户名错误
	else
	{
		sprintf(pbuf->data,"用户名或密码错误,请重新输入!\n");
	}
	send(connfd,pbuf,LEN_SMG,0);
}
int do_searchword(int connfd,MSG *pbuf,char word[])
{
	char line[500];//用来保存读取的单词及解释
	int ret;
	char *p;
	//打开文件,读取文件
	FILE *fp = fopen(PATH_DICT,"r");
	if(fp == NULL){
		printf("fopen fail\n");
		sprintf(pbuf->data,"抱歉,服务器打开词典失败\n");
		send(connfd,pbuf,LEN_SMG,0);
		return -1;
	}
	//打印出客户端要查询的单词
	int len = strlen(word);
	printf("%s,len = %d\n",word,len);
	//读文件来查询单词
	while(fgets(line,500,fp) != NULL)
	{
		ret = strncmp(line,word,len);

		if(ret < 0)
		{
			continue;
		}
		if(ret == 0|| line[len] != ' ')
		{
			//表示找到查询的单词
			p = line + len;
			while(*p == ' ')
			{
				p++;
			}
			//找到注释,跳过所有空格
			strcpy(pbuf->data,p);
			printf("%s\n",pbuf->data);
			send(connfd,pbuf,LEN_SMG,0);
			//拷贝完毕后,关闭文件
			fclose(fp);
			return 1;
		}
		else
		{
			break;
		}
	}
	strcpy(pbuf->data,"抱歉,没有找到该单词!\n");
	send(connfd,pbuf,LEN_SMG,0);
	fclose(fp);
	return 0;
}
//获取系统时间
char get_date(char *date){
	time_t t;
	struct tm *tp = NULL;
	time(&t);
	//进行时间格式转换
	tp = localtime(&t);
	sprintf(date,"%02d-%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 0;
}
//查询单词 Q
int do_query(int connfd,MSG *pbuf,sqlite3 *pdb){
	char word[64];
	int found = 0;
	char date[128];//用来保存系统时间
	char sql[1280];
	char line[200];//用来保存读取的单词及解释
	char *errmsg;
	int ret;
	//拿出pbuf结构体中要查询的单词
	strcpy(word,pbuf->data);
	found = do_searchword(connfd,pbuf,word);
	//找到单词,此时应该将用户名时间和单词插入到历史记录表中去
	if(found == 1)
	{
		//获取系统时间
		get_date(date);
		sprintf(sql,"insert into record values('%s','%s','%s');",pbuf->name,date,word);
		printf("%s\n",sql);
		if(sqlite3_exec(pdb,sql,NULL,NULL,&errmsg) != SQLITE_OK){
			printf("%s\n",errmsg);
			return -1;
		}
	}
	//表示没有找到
	else
	{
		sprintf(pbuf->data,"抱歉,没有找到该单词的意思\n");
	}
	//将查询结果发送给客户端
	send(connfd,pbuf,LEN_SMG,0);
	return 0;
}
//回调函数,每找到一条记录就执行一次该函数
int history_callback(void *arg,int f_column,char **f_value,char **f_name)
{	
	//f_column 记录包含的字段个数
	//f_value 每个记录的值
	//f_name 每个记录的字段名称

	int connfd;
	MSG pbuf;
	connfd = *(int *)arg;
	sprintf(pbuf.data,"%s,%s",f_value[1],f_value[2]);
	send(connfd,&pbuf,sizeof(pbuf),0);

	return 0;
}
//历史记录 H
void do_history(int connfd,MSG *pbuf,sqlite3 *pdb){
	char sql[1280];
	char *errmsg;
	sprintf(sql,"select * from record where name = '%s'",pbuf->name);
	//查询数据库
	if(sqlite3_exec(pdb,sql,history_callback,(void *)&connfd,&errmsg) != SQLITE_OK){
		printf("%s\n",errmsg);
		exit(-1);
	}
	else
		printf("请求成功\n");
	//所有的记录查询完毕后,给客户端发送结束信息
	pbuf->data[0] = '\0';
	send(connfd,pbuf,LEN_SMG,0);

}
void handler(int arg){
	wait(NULL);
}

//TCP通信
int main(){
	
	int serverfd,connfd;
	struct sockaddr_in serveraddr,clientaddr;
	socklen_t len = sizeof(SA);
	int cmd;
	char clean[SIZE]={0};
	pid_t pid;
	ssize_t bytes;
	//打开数据库
	sqlite3 *pdb;
	int ret;
	ret = sqlite3_open(PATH_DATA,&pdb);
	if(ret != SQLITE_OK){
		printf("sqlite3 error\n");
		return -1;
	}

	//创建套接字
	serverfd = socket(AF_INET,SOCK_STREAM,0);
	if(serverfd < 0){
		printf("fail to socket\n");
	}

	//绑定套接字
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(8888);
	serveraddr.sin_addr.s_addr = inet_addr("192.168.2.128");
	
	if(bind(serverfd,(SA *)&serveraddr,len) < 0){
		printf("fail to bind\n");
	}

	//监听服务端套接字
	if(listen(serverfd,10) < 0){
		printf("fail to listen\n");
	}
	signal(SIGCHLD,handler);//处理僵尸进程

	MSG pbuf;
	while(1){
		//创建新的套接字
		connfd = accept(serverfd,(SA *)&clientaddr,&len);
		if(connfd < 0){
			printf("accept\n");
			return -1;
		}
		//创建子进程
		pid = fork();
		if(pid < 0){
			printf("fail to fork\n");
			continue;
		}
		else if(pid == 0){//子进程
			close(serverfd);
			while(1){
				bytes = recv(connfd,&pbuf,LEN_SMG,0);
				if(bytes <= 0)
					break;
				switch(pbuf.type){
					case R:
						do_register(connfd,&pbuf,pdb);
						break;
					case L:
						do_login(connfd,&pbuf,pdb);
						break;
					case Q:
						do_query(connfd,&pbuf,pdb);
						break;
					case H:
						do_history(connfd,&pbuf,pdb);
						break;
					default:
						break;
				}
			}
			close(connfd);
			exit(1);
		}
		else//父进程退出
			close(connfd);
	}
	return 0;
}

六、效果展示

1、登录、注册界面效果:

C语言实现在线词典-TCP通信的方式实现_第6张图片

 C语言实现在线词典-TCP通信的方式实现_第7张图片

2、登录时,用户名或密码错误效果: 

C语言实现在线词典-TCP通信的方式实现_第8张图片

 3、查询单词效果:

C语言实现在线词典-TCP通信的方式实现_第9张图片

 4、输入#结束查询,返回上一级菜单:

C语言实现在线词典-TCP通信的方式实现_第10张图片

 5、查询历史记录:

C语言实现在线词典-TCP通信的方式实现_第11张图片

 6、两个界面退出效果:

C语言实现在线词典-TCP通信的方式实现_第12张图片

 C语言实现在线词典-TCP通信的方式实现_第13张图片

 

你可能感兴趣的:(服务器,运维,网络,c语言,tcp/ip)