网络编程项目——在线电子词典

目录

项目要求:

代码

服务器代码

客户端

运行截图

注册

首次注册

重复注册 

数据库 

 登录

正常登录

重复登录 

数据库 

 查询

数据库 

查询历史

 退出

数据库

​编辑 


项目要求:

  1. 登录注册功能,不能重复登录,重复注册

  2. 单词查询功能

  3. 历史记录功能,存储单词,意思,以及查询时间

  4. 基于TCP,支持多客户端连接

  5. 采用数据库保存用户信息与历史记录

  6. 将dict.txt的数据导入到数据库中保存。

  7. 按下ctrl+c退出客户端后,注销该客户端的登录信息

格式要求:

  1. main函数只跑逻辑,不允许跑功能代码

  2. 功能代码封装成函数

代码

服务器代码

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include <sqlite3.h>
#include<time.h>
//打印错误信息的宏函数
#define ERR_MSG(msg) do{\
	fprintf(stderr," __%d__",__LINE__);\
	perror(msg);\
}while(0);

#define PORT 8888             //1024~49151端口号
#define IP "192.168.31.31"   //本机IP,用ifcongfig查看

//定义协议
#define R 1  //注册
#define L 2  //登录
#define O 3  //注销退出
#define M 4  //修改
#define Q 5  //查询
#define H 6  //历史记录

typedef struct
{
	int type;   //存储协议
	char name[128];
	char data[128];
	int flag;  //判断是否登录
}MSG;

int do_register(int newfd,MSG *msg,sqlite3 *db);
int do_login(int newfd,MSG *msg,sqlite3 *db);
int	do_query(int newfd,MSG *msg,sqlite3 *db);
int do_history(int newfd,MSG *msg,sqlite3 *db);
int do_logout(int newfd,MSG *msg,sqlite3 *db);
int do_clint(int newfd,sqlite3 *db)
{
	MSG msg;
	memset(&msg,0,sizeof(msg));
	//服务器接收客户端信息,通过规定协议判断客户端请求
	while (recv(newfd,&msg,sizeof(MSG),0)>0)
	//while (1)
	{
		//recv(newfd,&msg,sizeof(msg),0);
		printf("type %d\n",msg.type);
		printf("name %s\n",msg.name);
		printf("data %s\n",msg.data);
		//根据结构体中的协议号调用程序响应处理
		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;
		case O:
			do_logout(newfd,&msg,db);
			break;

		default:			
			printf("GGGGGGGGG\n");
			break;
		}
	}
	char sql[128];
	sprintf(sql,"delete from msg where name='%s';",msg.name);
	//printf("%s\n",sql);
	char* errmsg = NULL;
	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	else
	{
		printf("用户注销>>>>X_X\n");
		strcpy(msg.data,"退出成功~");
	}
	printf("客户端退出...............\n");
	close(newfd);
	exit(0);
}

int do_register(int newfd,MSG *msg,sqlite3 *db)//注册函数
{
	char * errmsg;
	char sql[300];
	//查重
	int nrow, ncloumn;                         //数据库行数和列数
	char **resultp;                            //定义二级指针保存SOL语句执行的结果
	//拼接SOL语句,查询用户名和密码与数据库内容是否匹配
	sprintf(sql, "select * from msg where name='%s';", msg->name);
	//printf("%s\n", sql); //调试语句
	if (sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg)!=SQLITE_OK)
	{
		printf("%s\n",errmsg);
		return -1;
	}
	//printf("%d\n",nrow);
	if(nrow==1)
	{
		strcpy(msg->data,"重复注册!!!");
		send(newfd,msg,sizeof(*msg),0);
		return 1;
	}
	else
	{
		//printf("%s\n",sql);
		//printf("%s\n",msg->data);
		//printf("flag=%d\n",msg->flag);
		//向数据库中插入信息,flag为0,表示未登录
		sprintf(sql,"insert into msg values('%s',\'%s\',%d);",msg->name,msg->data,msg->flag);
		//printf("%s\n",sql);
		char* errmsg = NULL;
		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
			return -1;
		}
		//printf("insert success\n");
		//strcpy(msg->data,"插入成功");
		else
		{
			strcpy(msg->data,"注册成功");
		}
		if (send(newfd,msg,sizeof(*msg),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}

	}
}

int do_login(int newfd,MSG *msg,sqlite3 *db)//登录处理函数
{
	char * errmsg;
	char sql[300];
	//查重
	//char *errmsg;
	int nrow, ncloumn;                         //数据库行数和列数
	char **resultp;                            //定义二级指针保存SOL语句执行的结果
	//拼接SOL语句,查询用户名和密码与数据库内容是否匹配
	sprintf(sql, "select * from msg where name='%s' and passwd='%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;
	}
	if(nrow==1)
	{
		//if(resultp[5]==1)
		if(strcmp(resultp[5],"1")==0)//flag=1表示用户已登录 返回信息给客户端
		{
			strcpy(msg->data,"该用户已登录,洗洗睡吧");
			send(newfd,msg,sizeof(*msg),0);
			return -1;
		}
		bzero(sql,sizeof(sql));
		msg->flag=1;
		sprintf(sql,"update msg set flag=%d where name='%s' and passwd='%s';",msg->flag,msg->name,msg->data);
		//printf("%s\n",sql);
		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
			return -1;
		}
		strcpy(msg->data,"OK");
		
		send(newfd,msg,sizeof(*msg),0);
		return 1;
	}
	else
	{
		strcpy(msg->data,"!!!账号密码有误!!!");
		send(newfd,msg,sizeof(MSG),0);
	}
	return 0;

}

int do_query(int newfd,MSG *msg,sqlite3 *db)//查询单词
{
	char tm[128];
	char * errmsg;
	char sql[300];
	//查重
	//char *errmsg;
	int nrow, ncloumn;                         //数据库行数和列数
	char **resultp;                            //定义二级指针保存SOL语句执行的结果
	//拼接SOL语句,查询用户名和密码与数据库内容是否匹配
	sprintf(sql, "select * from dict where word='%s';", msg->data);
	//printf("%s\n", sql); //调试语句
	if (sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg)!=SQLITE_OK)
	{
		printf("%s\n",errmsg);
		return -1;
	}
	//printf("%d\n",nrow);
	if(nrow==1)
	{
		time_t t;
		struct tm *info = NULL;
		t = time(NULL);
		info = localtime(&t);
		//\r转义字符,将光标重新移动回开头
		sprintf(tm,"%d-%02d-%02d %02d:%02d:%02d\r", \
				info->tm_year+1900, info->tm_mon+1, info->tm_mday,\
				info->tm_hour, info->tm_min, info->tm_sec);
		//strcpy(tm,asctime(t));
		//printf("%s\n",tm);
		//printf("zhongwen  %s\n",resultp[3]);
		sprintf(sql,"insert into history values('%s',\'%s\','%s');",msg->name,tm,msg->data);
		//printf("%s\n",sql);
		char* errmsg = NULL;
		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
			return -1;
		}

		strcpy(msg->data,resultp[3]);
		send(newfd,msg,sizeof(*msg),0);
		return 1;
	}
	else
	{
		strcpy(msg->data,"NO");
		send(newfd,msg,sizeof(MSG),0);
	}
	return 0;
}
int do_history(int newfd,MSG *msg,sqlite3 *db)//历史记录查询
{
	printf("test history\n");
	//char his[256];//拼接历史记录
	char * errmsg;
	char sql[300];
	//查重
	//char *errmsg;
	int nrow, ncloumn;                         //数据库行数和列数
	char **resultp;                            //定义二级指针保存SOL语句执行的结果
	//拼接SOL语句,查询用户名和密码与数据库内容是否匹配
	//sprintf(sql, "select * from history;");
	strcpy(sql,"select * from history;");
	//printf("%s\n", sql); //调试语句
	if (sqlite3_get_table(db,sql,&resultp,&nrow,&ncloumn,&errmsg)!=SQLITE_OK)
	{
		printf("%s\n",errmsg);
		return -1;
	}
	if(nrow!=0)
	{
		//printf("query successful.\n");
		//printf("test hang:%d  lie:%d\n",nrow,ncloumn);
		int i=0;
		for (i=3; i<(nrow+1)*ncloumn; i+=3)
		{
			bzero(msg->data,sizeof(msg->data));
		/*	printf("ceshi %s\n",msg->data);
			printf("%s\n",resultp[i]);
			printf("%s\n",resultp[i+1]);
			printf("%s\n",resultp[i+2]);*/

			sprintf(msg->data,"%s %s %s",resultp[i],resultp[i+2],resultp[i+1]);//拼接记录信息写入结构体
			printf("%s\n",msg->data);
			if(send(newfd,msg,sizeof(*msg),0)<0)
			{
				ERR_MSG("send");
				return -1;
			}
			bzero(msg->data,sizeof(msg->data));

		}
		msg->data[0]='\0';
		if(send(newfd,msg,sizeof(MSG),0)<0)
		{
			ERR_MSG("send");
			return -1;
		}
		return 1;
	}
	else
		return 0;

}
int do_logout(int newfd,MSG* msg,sqlite3 *db)//注销函数
{
	char sql[128];
	sprintf(sql,"delete from msg where name='%s';",msg->name);//从数据库中删除用户信息
	printf("%s\n",sql);
	char* errmsg = NULL;
	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	else
	{
		printf("用户已注销,客户端GG。X_X\n");
		strcpy(msg->data,"退出成功~");
	}
	if(send(newfd,msg,sizeof(MSG),0)<0)
	{
		ERR_MSG("send");
		return -1;
	}


}
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if (sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("create socket success\n");

	//允许端口快速复用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	//填充地址信息结构体,真实地址信息结构体与协议族相关
	//AF_INET,所以详情看》man 7 ip
	struct sockaddr_in sin;
	sin.sin_family           =AF_INET;
	sin.sin_port             =htons(PORT);        			//网络字节序
	sin.sin_addr.s_addr      =inet_addr(IP);               	//网络字节序的IP地址
	//将地址信息结构体绑定到套接字上
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");
	
	//将套接字设置为被动监听状态,让内核去监听是否有客户端链接
	if (listen(sfd,10)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	//打开数据库
	sqlite3* db = NULL;
	if(sqlite3_open("./dict.db", &db) != SQLITE_OK)
	{
		printf("err_code:%d\n", sqlite3_errcode(db));
		printf("errmsg:%s\n", sqlite3_errmsg(db));
		fprintf(stderr, "__%d__ sqlite3_open failed\n", __LINE__);
		return -1;
	}
	printf("sqlite3_open:dict.db success\n");
	
	//创建信息表
	char* sql = "create table if not exists msg ( name char, passwd char,flag int);" ;
	char* errmsg = NULL;
	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	printf("create table msg success\n");

	//创建历史记录表
	char* sql2 = "create table if not exists history (name char, datatime char, word char);" ;
//char* errmsg2 = NULL;
	if(sqlite3_exec(db, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_exec:%s\n", __LINE__, errmsg);
		return -1;
	}
	printf("create table success\n");	

	struct sockaddr_in cin;
	socklen_t addrlen= sizeof(cin);
	//从已完成的队列头中,取出一个客户端的信息,创建生成一个新的套接字文件描述符
	//该文件描述符才是与客户端通信的文件描述符!!!!
/*	int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
	if (newfd<0)
	{
		perror("accept");
		return -1;
	}*/
	//网络字节序的IP--->点分十进制 网络字节序port转换成主机字节序
	///printf("[%s : %d] newfd = %d \n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
	printf("***************************等待客户端链接*****************************\n");
	while (1)
	{
		int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if (newfd<0)
		{
			perror("accept");
			return -1;
		}
printf("[%s : %d] newfd = %d \n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
		pid_t pid=fork();
		if (pid<0)
		{
			ERR_MSG("fork");
			exit(-1);
		}else if(pid==0)//子进程
		{
			//处理客户端请求
			close(sfd);
			do_clint(newfd,db);
		}else
		{
			//父进程
			close(newfd);
		}
	close(newfd);
	}
	
	close(sfd);
//	close(newfd);
	return 0;
}

客户端

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#define ERR_MSG(msg) do{\
	fprintf(stderr," __%d__",__LINE__);\
	perror(msg);\
}while(0);

#define PORT 8888             //1024~49151端口号
#define IP "192.168.31.31"   //本机IP,用ifcongfig查看
typedef struct
{
	int type;   //存储协议
	char name[128];
	char data[128];
	int flag;  //判断是否登录
}MSG;

//定义协议
#define R 1  //注册
#define L 2  //登录
#define O 3  //注销退出
#define Q 5  //查询
#define H 6  //历史记录

//注册函数
int do_register(int sfd,MSG *msg)
{
	//printf("%ld\n",sizeof(*msg));
	msg->type=R;
	msg->flag=0;
	printf("请输入账号>>>");//账号输入
	scanf("%s",msg->name);
	getchar();
	printf("请输入密码>>>");//密码输入
	scanf("%s",msg->data);
	getchar();
	//printf("ceshi %s\n",msg->name);
	//printf("ceshi %s\n",msg->data);
	//memcpy(send_buf,&msg,sizeof(msg));
	//客户端先发送
	if (send(sfd,msg,sizeof(*msg),0)<0)
	//if (send(sfd,send_buf,sizeof(send_buf),0)<0)
	{
		ERR_MSG("send");
		return -1;
	}
	//memset(&msg,0,sizeof(msg));
	//后接收
	if (recv(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("recv");
		return -1;
	}
	printf("%s\n",msg->data);
	return 0;
}

//登录函数
int do_login(int sfd,MSG *msg)
{
	msg->type=L;
	printf("请输入账号>>>");//账号输入
	scanf("%s",msg->name);
	getchar();
	printf("请输入密码>>>");//密码输入
	scanf("%s",msg->data);
	getchar();
	//客户端先发送
	if (send(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("send");
		return -1;
	}
	//后接收
	if (recv(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("recv");
		return -1;
	}
	//if(strncmp(msg->data,"OK",3)==0)
	if(strcmp(msg->data,"OK")==0)
	{
		printf("***************登陆成功****************\n\n");
	//	printf("***************************************\n");
		return 1;
	}else
	{
		//printf("登录失败\n");
		printf("%s\n",msg->data);
		return 0;
	}
	return 0;

}

//退出登录函数
int do_logout(int sfd,MSG *msg)
{
	if (msg->flag==0)
	{
		printf("用户未登录\n");
		return -1;
	}
	msg->type=O;//发送退出登录协议
	printf("%d\n",msg->type);
	//客户端先发送
	if (send(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("send");
		return -1;
	}
	//后接收
	if (recv(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("recv");
		return -1;
	}
	printf("%s\n",msg->data);
	return 0;
}

//查询函数
int do_query(int sfd,MSG *msg)
{
	msg->type=Q;
	//输入要查询的单词
	printf("请输入要查询的单词\n");
	fgets(msg->data,sizeof(msg->data),stdin);
	msg->data[strlen(msg->data)-1]='\0';
	//printf("test %s\n",msg->data);
	//客户端先发送
	if (send(sfd,msg,sizeof(MSG),0)<0)
	{
		ERR_MSG("send");
		return -1;
	}
	//后接收
	if (recv(sfd,msg,sizeof(*msg),0)<0)
	{
		ERR_MSG("recv");
		return -1;
	}
	if (strcmp(msg->data,"NO")==0)
	{
		printf("XXXXXXX未查询到对应单词XXXXXXXXX\n");
	}else
	{
		printf("中文意思为:%s\n",msg->data);
	}
	return 0;
}

//查询历史记录
int do_history(int sfd,MSG *msg)
{
	msg->type=H;
	//printf("test %d\n",msg->type);
	if (send(sfd,msg,sizeof(MSG),0)<0)
	{
		printf("发送失败\n");
		return -1;
	}
	while (1)
	{
		if (recv(sfd,msg,sizeof(MSG),0)<0)
		{
			ERR_MSG("recv");
			return -1;
		}
		if (msg->data[0]==0)
		{
			break;
		}
		printf("%s\n",msg->data);
	}
	return 0;
}

int main(int argc, const char *argv[])
{
	MSG msg;
	//printf("%ld\n",sizeof(msg));

	//创建流式套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if (sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	//绑定客户端的地址信息结构体》非必须填写
	//填充要连接的服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family     =AF_INET;
	sin.sin_port       =htons(PORT);
	sin.sin_addr.s_addr=inet_addr(IP);


	//链接服务器
	if (connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("connect");
		return -1;
	}

start:	
	while (1)    //登录交互界面
	{
		printf("***************************************\n");
		printf("****************1:注册*****************\n");
		printf("****************2:登录*****************\n");
		printf("****************3:退出*****************\n");
		printf("***************************************\n");
		printf("请选择>>>");
		int n;
		scanf("%d",&n);
		getchar();//吸收垃圾字符
		switch(n)
		{
			case 1:
				do_register(sfd,&msg);
				break;
			case 2:
				if(do_login(sfd,&msg)==1)
				goto next;
				break;
			case 3:
				close(sfd);
				exit(0);
				break;
			default:
				printf("请输入正确选项!!!\n");
		}
	}
next:
	while (1)
	{
		printf("***************************************\n");
		printf("****************1:查询翻译*************\n");
		printf("****************2:查询历史*************\n");
		printf("****************3:退出登录*************\n");
		printf("***************************************\n");
		printf("请选择>>>");
		int n;
		scanf("%d",&n);
		getchar();//吸收垃圾字符
		switch(n)
		{
			case 1:
				do_query(sfd,&msg);
				break;
			case 2:
				do_history(sfd,&msg);
				break;
			case 3:
				do_logout(sfd,&msg);
				goto start;
				break;
			default:
				printf("请输入正确选项!!!\n");
				break;
		}
	}
	close(sfd);
	return 0;
}

运行截图

注册

首次注册

重复注册 

数据库 

 登录

正常登录

重复登录 

数据库 

登陆后flag为1

 查询

数据库 

查询后将信息插入数据库

查询历史

 退出

按程序退出或者ctrl+c结束,都将注销用户,从数据库中删除用户信息

数据库

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值