电子词典项目

这是一个电子词典项目的实现,包括服务器端和客户端代码。服务器端实现了登录注册、单词查询、历史记录等功能,使用SQLite数据库存储用户信息和查询历史。客户端则提供了用户交互界面,支持注册、登录、查询单词和查看历史记录。项目基于TCP协议,确保多客户端连接的稳定性。
摘要由CSDN通过智能技术生成

项目:电子词典项目需求

项目要求:
1、登录注册功能,不能重复登录,重复注册
2、单词查询功能
3、历史记录功能,存储单词,意思,以及查询时间
4、基于TCP,支持多客户端连接
5、采用数据库保存用户信息与历史记录
6、将dict.txt的数据导入到数据库中保存。
7、按下ctrl+c退出客户端后,注销该客户端的登录信息

格式要求:
main函数只跑逻辑,不允许跑功能代码
功能代码封装成函数

服务器端代码

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

#define ERR_MSG(msg)                           \
	do                                         \
	{                                          \
		fprintf(stderr, "line:%d ", __LINE__); \
		perror(msg);                           \
	} while (0)

#define PORT 6666			//1024~49151
#define IP "192.168.31.115" //IP地址,本机IP ifconfig

//信息传递协议
struct communication
{
	char mode;	   //功能模式
	char name[32]; //用户名
	char msg[512]; //查询的单词,返回的释义,历史记录等
};

//僵尸进程的回收
void zombie_callBack(int sig)
{
	while (waitpid(-1, NULL, WNOHANG) > 0)
		;
}

//单词文档导入到数据库中
int word_import()
{
	//读取txt文件到数据库
	FILE *fp = fopen("./dict.txt", "r"); //只读方式打开txt文件
	if (NULL == fp)
	{
		perror("fopen");
		return -1;
	}
	//创建并打开数据库
	sqlite3 *db = NULL;
	if (sqlite3_open("./dict.db", &db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("sqlite3_open sucess __%d__\n", __LINE__);

	//创建表格,[数据库中代码怎么写,此处就怎么写]
	char sql[128] = "create table if not exists dictionary (word char, mean char);";
	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("table dictionary create sucess __%d__ \n", __LINE__);

	printf("数据库写入中......\n");
	//读取txt文件中的字符串
	char buf[128] = "";
	char buf_word[32] = ""; //英文单词
	char buf_prop[32] = ""; //词性
	char buf_mean[32] = ""; //单词释义
	char *p_temp = NULL;
	while (1)
	{
		bzero(buf, sizeof(buf));
		bzero(buf_word, sizeof(buf_mean));
		bzero(buf_mean, sizeof(buf_mean));
		if (fgets(buf, sizeof(buf), fp) == NULL) //遇到换行会停止读取,并自动补\0
		{
			break; //读取结束,fgets返回值为NULL
		}
		//写入到数据库中
		p_temp = buf;
		int i = 0;
		int j = 0;
		//将读取到的一整串字符串分开
		while (1)
		{
			if (*p_temp == ' ' && *(p_temp + 1) == ' ') //满足此条件停止单词的读取
			{
				//词性的寻找
				while (1)
				{
					p_temp++;
					if (*p_temp != ' ')
						break;
				}
				break;
			}
			buf_word[i] = *p_temp;
			i++;
			p_temp++;
		}
		strcpy(buf_mean, p_temp);
		buf_mean[strlen(buf_mean) - 1] = 0;

		//此时已经可以循环分解开单词和词性以及释义
		//写入到数据库中
		char temp[128] = "";
		sprintf(temp, "insert into dictionary values (\"%s\", \"%s\");", buf_word, buf_mean);

		char *errmsg = NULL;
		sqlite3_exec(db, temp, NULL, NULL, &errmsg);
	}

	printf("数据库写入成功\n");
	//关闭数据库,释放内存空间
	if (sqlite3_close(db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);

	//关闭fopen打开的英文单词只读文件
	fclose(fp);
	return 0;
}

//单词数据库文件的初始化
int word_init()
{
	if (access("./dict.txt", F_OK) == -1)
	{
		printf("文本文档文件不存在\n");
	}
	else
	{
		if (access("./dict.db", F_OK) == 0)
		{
			printf("数据库文件已经存在\n");
		}
		else
		{
			word_import();
		}
	}
}

//注册信息的处理
int do_sign_up(int newfd, struct sockaddr_in cin, struct communication rcv_msg)
{
	ssize_t res = -1;
	//创建并打开用户信息数据库[查询单词和时间]  //如果存在则直接打开,不存在会创建
	sqlite3 *db2 = NULL;
	if (sqlite3_open("./user.db", &db2) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db2), sqlite3_errmsg(db2));
		return -1;
	}
	// printf("user_sqlite3_open sucess __%d__\n", __LINE__);
	//创建所有的用户信息表格,存储所有用户和密码,用户名为主键
	char sql2[1512] = "";
	sprintf(sql2, "create table if not exists all_msg (name char primary key, password char, status char);");
	char *errmsg = NULL;
	if (sqlite3_exec(db2, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
	{

		fprintf(stderr, "__%d__ sqlite3_exec : %s\n", __LINE__, errmsg);
		return -1;
	}
	// printf("table all_msg create sucess __%d__ \n", __LINE__);

	//插入信息的调用,如果插入失败,则表明重复注册
	bzero(sql2, sizeof(sql2));
	sprintf(sql2, "insert into all_msg values (\"%s\", \"%s\", \"N\");", rcv_msg.name, rcv_msg.msg);
	strcpy(rcv_msg.msg, "用户注册成功");
	if (sqlite3_exec(db2, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		if (sqlite3_errcode(db2) == 19)
		{
			strcpy(rcv_msg.msg, "该用户重复注册");
		}
	}

	//查询完毕,发送查询后的信息给客户端
	if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}

	// printf("send success\n");

	//注册完毕后,应该再创建一个记录查询历史记录的表格[单词, 查询时间]
	sprintf(sql2, "create table if not exists %s (word char, time char);", rcv_msg.name);
	if (sqlite3_exec(db2, sql2, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_exec : %s\n", __LINE__, errmsg);
		return -1;
	}

	//关闭用户信息数据库,释放内存空间
	if (sqlite3_close(db2) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db2), sqlite3_errmsg(db2));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);

	return 0;
}

//数据库的查看遍历
int do_select(sqlite3 *db, struct communication rcv_msg)
{
	char sql[128] = "select name,password from all_msg";

	char **pres = NULL; //遇到三级指针,在外面定义一个二级指针;函数运行结束,该指针存储最终查询的结果
	int row, column;	//结果的行数 和 结果的列数
	char *errmsg = NULL;
	//查询函数的调用
	if (sqlite3_get_table(db, sql, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_get_table: %s\n", __LINE__, errmsg);
		return -1;
	}
	// printf("sqlite3_get_table success __%d__\n", __LINE__);

	//printf("row=%d column=%d\n", row, column); //查询出来整个table的行数和列数

	//row中只有记录行数,没有表头
	//而pres中包含表头的那一行,所以打印的时候需要将表头的那一行加上
	char name[32] = "";
	char password[32] = "";
	int flag = 0;
	for (int i = 1; i < (row + 1) * column; i++)
	{
		//printf("%s\t", pres[i]);
		if (i % column == column - 1)
		{
			strcpy(name, pres[i - 1]);
			strcpy(password, pres[i]);
			// printf("=== %s %s", name, password);
			// putchar(10);  //此时name和password都遍历结束,可以在这里判断

			//判断用户名和密码是否正确
			if (strcmp(name, rcv_msg.name) == 0) //用户名存在数据库中,可以进一步执行登录操作
			{
				if (strcmp(password, rcv_msg.msg) == 0) //密码也和数据库中数据相同
				{
					strcpy(rcv_msg.msg, "登录成功");
					flag = 1; //如果登陆成功,返回值为1
				}
				else
				{
					strcpy(rcv_msg.msg, "用户存在,但是密码不正确");
					flag = 2;
				}
				break;
			}
		}
	}

	//printf("==========%d========%s\n", __LINE__, rcv_msg.msg);

	//释放查询到的结果
	sqlite3_free_table(pres);
	pres = NULL;

	return flag;
}

int is_online(sqlite3 *db, struct communication rcv_msg)
{
	char sql[128] = "select name,status from all_msg";
	char **pres = NULL;
	int row, column;
	char* errmsg = NULL;
	//查询函数的调用
	if (sqlite3_get_table(db, sql, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_get_table: %s\n", __LINE__, errmsg);
		return -1;
	}
	// printf("sqlite3_get_table success __%d__\n", __LINE__);
	for(int i = 0; i < (row+1)*column; i++)
	{
		if(i % column == column - 1)
		{
			if(strcmp(pres[i], "Y") == 0)  //表示在线
			{
				//释放查询到的结果
				sqlite3_free_table(pres);
				pres = NULL;
				return 1;
			}
		}
	}
	//释放查询到的结果
	sqlite3_free_table(pres);
	pres = NULL;
	return 0;
}

//登录信息的处理
int do_log_on(int newfd, struct sockaddr_in cin, struct communication rcv_msg)
{
	ssize_t res = -1;
	//创建并打开用户信息数据库,所有用户名和密码
	sqlite3 *db = NULL;
	if (sqlite3_open("./user.db", &db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("user_sqlite3_open sucess __%d__\n", __LINE__);

	//循环遍历数据库,查看是否可以登录
	if (do_select(db, rcv_msg) == 1)
	{
		// printf("==========%d=====%s===%s\n", __LINE__, rcv_msg.name, rcv_msg.msg);   //输出结果为ff和gg,形参未改变实参
		//查询完毕,发送查询后的信息给客户端
		strcpy(rcv_msg.msg, "登录成功");
		//可以登录成功的话,先查询是否重复登录,如果不重复,再实行后续语句
		if(is_online(db,rcv_msg) == 1)   //表示该用户在线
		{
			strcpy(rcv_msg.msg, "重复登录");
		}
		else
		{
			//登录成功后,修改用户的在线状态,更新为在线[没有必要再创建一个新的表格存储状态信息]
			char sql[128] = "";
			sprintf(sql, "update all_msg set status=\"Y\" where name=\"%s\";", rcv_msg.name);
			char *errmsg = NULL;
			//执行状态更新语句
			if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
			{

				fprintf(stderr, "__%d__ sqlite3_exec : %s\n", __LINE__, errmsg);
				return -1;
			}
		}

		if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		// printf("send success\n");
	}
	else if (do_select(db, rcv_msg) == 2)
	{
		strcpy(rcv_msg.msg, "用户存在,但是密码不正确");
		if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		// printf("send success\n");
	}
	else if (do_select(db, rcv_msg) == 0)
	{
		strcpy(rcv_msg.msg, "用户不存在,请先注册");
		if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		// printf("send success\n");
	}

	//关闭用户信息数据库,释放内存空间
	if (sqlite3_close(db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);

	return 0;
}

//记录查询时间
char *time_look(char *buf_time)
{
	time_t time_now;
	time(&time_now);  //获取当前时间的秒数
	//传入的形参为当前文件的修改秒数
	struct tm * info_time = localtime(&time_now);
	sprintf(buf_time, "%d-%d-%d %d:%d:%d", info_time->tm_year+1900, info_time->tm_mon+1,\
			info_time->tm_mday, info_time->tm_hour, info_time->tm_min, info_time->tm_sec);
	return buf_time;
}

//查询单词的处理
int look_up_word(int newfd, struct sockaddr_in cin, struct communication rcv_msg)
{
	ssize_t res = -1;
	//打开词典信息数据库
	sqlite3 *db = NULL;
	if (sqlite3_open("./dict.db", &db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("user_sqlite3_open sucess __%d__\n", __LINE__);
	//查询遍历字典库,是否存在的数据库中,存在的话,返回单词释义
	char sql[128] = "select * from dictionary";

	char **pres = NULL; //遇到三级指针,在外面定义一个二级指针;函数运行结束,该指针存储最终查询的结果
	int row, column;	//结果的行数 和 结果的列数
	char *errmsg = NULL;
	//查询函数的调用
	if (sqlite3_get_table(db, sql, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_get_table: %s\n", __LINE__, errmsg);
		return -1;
	}
	// printf("sqlite3_get_table success __%d__\n", __LINE__);

	char temp[128] = "";
	strcpy(temp, rcv_msg.msg);

	//row中只有记录行数,没有表头
	//而pres中包含表头的那一行,所以打印的时候需要将表头的那一行加上
	char word[128] = "";
	char mean[128] = "";
	int flag = 0;
	for (int i = 1; i < (row + 1) * column; i++)
	{
		if (i % column == column - 1)
		{
			bzero(word, sizeof(word));
			bzero(mean, sizeof(mean));
			strcpy(word, pres[i - 1]);
			strcpy(mean, pres[i]);
			// putchar(10);  //此时name和password都遍历结束,可以在这里判断
			
			//判断查询的单词是否在数据库中
			if (strcmp(word, temp) == 0)
			{
				strcat(temp, "     ");
				strcat(temp, mean);
				flag = 1;
				/****************************查询时间的写入*********************************/
				//打开对应的数据库
				//创建并打开用户信息数据库[查询单词和时间]  //如果存在则直接打开,不存在会创建
				sqlite3 *db1 = NULL;
				if (sqlite3_open("./user.db", &db1) != SQLITE_OK)
				{
					fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db1), sqlite3_errmsg(db1));
					return -1;
				}
				// printf("user_sqlite3_open sucess __%d__\n", __LINE__);

				//查询单词和查询时间的写入
				char sql3[128] = "";
				char buf_time[32] = "";
				time_look(buf_time);
				sprintf(sql3, "insert into \"%s\" values (\"%s\", \"%s\");", rcv_msg.name, temp, buf_time);
				strcpy(rcv_msg.msg, "用户注册成功");
				if (sqlite3_exec(db1, sql3, NULL, NULL, &errmsg) != SQLITE_OK)
				{
					if (sqlite3_errcode(db1) == 19)
					{
						strcpy(rcv_msg.msg, "该用户重复注册");
					}
				}

				//关闭数据库
				if (sqlite3_close(db1) != SQLITE_OK)
				{
					fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db1), sqlite3_errmsg(db1));
					return -1;
				}
				// printf("sqlite3_close sucess __%d__\n", __LINE__);

				/**************************************************************************/		
				strcpy(rcv_msg.msg, temp);
				if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)   //发送给客户端查询到的结果
				{
					ERR_MSG("send");
					return -1;
				}
				// printf("send success\n");
				break;
			}
			else
			{
				flag = 0;
			}
			
		}
	}
	if(flag == 0)
	{
		strcat(temp, "     查询的单词不存在");
		strcpy(rcv_msg.msg, temp);
		if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)   //发送给客户端查询到的结果
		{
			ERR_MSG("send");
			return -1;
		}
		// printf("send success\n");
	}

	//释放查询到的结果
	sqlite3_free_table(pres);
	pres = NULL;

	//关闭词典信息数据库,释放内存空间
	if (sqlite3_close(db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);
}

//历史记录查询
int history_check(int newfd, struct sockaddr_in cin, struct communication rcv_msg)
{
	//打开对应的数据库,//如果存在则直接打开,不存在会创建,查询该数据库下对应的表格文件
	sqlite3 *db1 = NULL;
	if (sqlite3_open("./user.db", &db1) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db1), sqlite3_errmsg(db1));
		return -1;
	}
	// printf("user_sqlite3_open sucess __%d__\n", __LINE__);

	//遍历历史记录的表格,然后输出发送
	char sql1[128] = "";
	sprintf(sql1, "select * from \"%s\"", rcv_msg.name);
	char **pres = NULL; //遇到三级指针,在外面定义一个二级指针;函数运行结束,该指针存储最终查询的结果
	int row, column;	//结果的行数 和 结果的列数
	char *errmsg = NULL;
	//查询函数的调用
	if (sqlite3_get_table(db1, sql1, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_get_table: %s\n", __LINE__, errmsg);
		return -1;
	}
	// printf("sqlite3_get_table success __%d__\n", __LINE__);

	//row中只有记录行数,没有表头
	//而pres中包含表头的那一行,所以打印的时候需要将表头的那一行加上
	char buf_word[40] = "";
	char buf_time[20] = "";
	char temp[64] = "";
	char buf_snd[500] = "";
	int flag = 0;
	for (int i = 2; i < (row + 1) * column; i++)
	{
		if (i % column == column - 1)
		{
			bzero(buf_word,sizeof(buf_word));
			bzero(buf_time,sizeof(buf_time));
			bzero(temp, sizeof(temp));

			strcpy(buf_word, pres[i - 1]);
			strcpy(buf_time, pres[i]);

			sprintf(temp, "%s   %s\n",buf_word,buf_time);
			strcat(buf_snd,temp);
		}
	}

	//遍历查询结果结束,所有历史记录遍历完毕,将信息发送到客户端
	strcpy(rcv_msg.msg, buf_snd);
	if (send(newfd, &rcv_msg, sizeof(rcv_msg), 0) < 0)   //发送给客户端查询到的结果
	{
		ERR_MSG("send");
		return -1;
	}
	// printf("send success\n");

	//释放查询到的结果
	sqlite3_free_table(pres);
	pres = NULL;

	//关闭数据库
	if (sqlite3_close(db1) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db1), sqlite3_errmsg(db1));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);
}

//退出登录,对应客户的在线状态修改为下线
int log_out(int newfd, struct sockaddr_in cin, struct communication rcv_msg)
{
	//打开用户信息数据库,修改用户在线状态为下线
	sqlite3 *db = NULL;
	if (sqlite3_open("./user.db", &db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("user_sqlite3_open sucess __%d__\n", __LINE__);

	//修改用户下线状态
	char sql[128] = "";
	sprintf(sql, "update all_msg set status=\"N\" where name=\"%s\";", rcv_msg.name);
	char *errmsg = NULL;
	//执行状态更新语句
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{

		fprintf(stderr, "__%d__ sqlite3_exec : %s\n", __LINE__, errmsg);
		return -1;
	}

	//关闭用户信息数据库,释放内存空间
	if (sqlite3_close(db) != SQLITE_OK)
	{
		fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
		return -1;
	}
	// printf("sqlite3_close sucess __%d__\n", __LINE__);

	return 0;
}

//处理客户端信息
int deal_cli_msg(int newfd, struct sockaddr_in cin)
{

	struct communication rcv_msg;
	while (1)
	{
		ssize_t res = -1;
		//接收客户端发送过来消息,然后判断消息类型,做出相应的处理方式
		res = recv(newfd, &rcv_msg, sizeof(rcv_msg), 0);
		if (res < 0)
		{
			ERR_MSG("recv");
			return -1;
		}
		else if (0 == res)
		{
			fprintf(stderr, "[%s : %d] newfd=%d 客户端下线\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);

			//创建并打开用户信息数据库,所有用户名和密码
			sqlite3 *db = NULL;
			if (sqlite3_open("./user.db", &db) != SQLITE_OK)
			{
				fprintf(stderr, "__%d__ sqlite3_open : %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
				return -1;
			}
			// printf("user_sqlite3_open sucess __%d__\n", __LINE__);


			//修改用户下线状态
			char sql[128] = "";
			sprintf(sql, "update all_msg set status=\"N\" where name=\"%s\";", rcv_msg.name);
			char *errmsg = NULL;
			//执行状态更新语句
			if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
			{

				fprintf(stderr, "__%d__ sqlite3_exec : %s\n", __LINE__, errmsg);
				return -1;
			}

			//关闭用户信息数据库,释放内存空间
			if (sqlite3_close(db) != SQLITE_OK)
			{
				fprintf(stderr, "__%d__ sqlite3_close: %d | %s\n", __LINE__, sqlite3_errcode(db), sqlite3_errmsg(db));
				return -1;
			}
			// printf("sqlite3_close sucess __%d__\n", __LINE__);

			return -1;
		}
		printf("%d---[%s : %d] newfd=%d : %s--%s\n", __LINE__,inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, rcv_msg.name, rcv_msg.msg);
		//判断客户端消息类型,然后做出相应的处理操作
		switch (rcv_msg.mode)
		{
		case 'S':
			//客户端注册
			do_sign_up(newfd, cin, rcv_msg); 
			break;
		case 'L':
			//客户端登录
			do_log_on(newfd, cin, rcv_msg); 
			break;
		case 'C':
			//客户端查询单词 [查询单词的时候就记录查询时间]
			look_up_word(newfd, cin, rcv_msg);
			break;
		case 'H':
			//查询历史记录
			history_check(newfd, cin, rcv_msg);
			break;
		case 'E':
			//退出登录
			log_out(newfd, cin, rcv_msg);
			break;
		default:
			printf("客户端信息错误");
			break;
		}
	}

	return 0;
}

//主函数框架
int main(int argc, const char *argv[])
{
	//捕获17号信号
	__sighandler_t s = signal(SIGCHLD, zombie_callBack);
	if (SIG_ERR == s)
	{
		ERR_MSG("signal");
		return -1;
	}

	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success  sfd = %d\n", sfd);

	//设置允许端口快速被重用
	int resue = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}

	//填充服务器的地址信息结构体
	//真实的地址信息结构体根据地址族执行,AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;			 //必须填AF_INET;
	sin.sin_port = htons(PORT);			 //端口号的网络字节序,1024~49151
	sin.sin_addr.s_addr = inet_addr(IP); //IP地址的网络字节序,ifconfig查看

	//绑定---必须绑定
	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success __%d__\n", __LINE__);

	//将套接字设置为被动监听状态
	if (listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success __%d__\n", __LINE__);

	struct sockaddr_in cin; //存储连接成功的客户端的地址信息
	socklen_t addrlen = sizeof(cin);
	int newfd = -1;
	pid_t cpid = 0;
	struct communication rcv_msg; //结构体格式的通信协议

	while (1)
	{
		//父进程专门负责连接
		newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
		if (newfd < 0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s : %d] newfd=%d 客户端连接成功\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);

		//创建一个子进程,用于与客户端交互
		cpid = fork();
		if (0 == cpid) //子进程运行
		{
			//--->专门负责与客户端交互,所以客户端必须先连接成功,才能创建子进程
			close(sfd); //对于子进程而言,sfd没有用

			deal_cli_msg(newfd, cin); //处理客户端信息

			close(newfd);
			exit(0); //退出子进程,因为子进程只负责交互
		}
		else if (cpid > 0)
		{
			close(newfd); //对于父进程而言,newfd没用
		}
		else
		{
			ERR_MSG("fork");
			return -1;
		}
	}

	//关闭所有套接字文件描述符
	close(sfd);
	return 0;
}

客户端代码

#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>

#define ERR_MSG(msg)                           \
	do                                         \
	{                                          \
		fprintf(stderr, "line:%d ", __LINE__); \
		perror(msg);                           \
	} while (0)

#define PORT 6666			//1024 -- 49151
#define IP "192.168.31.115" //IP地址,本机IP,ifconfig查看

int function();

//信息传递协议
struct communication
{
	char mode;	   //功能模式
	char name[32]; //用户名
	char msg[512]; //查询的单词,返回的释义,历史记录等
};

int menu(int num)
{
	if (1 == num) //一级菜单
	{
		printf("---------------------------\n");
		printf("----------1.注册-----------\n");
		printf("----------2.登录-----------\n");
		printf("-------3.退出客户端---------\n");
		printf("---------------------------\n");
	}
	else if (2 == num)
	{
		printf("---------------------------\n");
		printf("----------1.查询-----------\n");
		printf("----------2.历史-----------\n");
		printf("---3.返回上一级,退出登录 --\n");
		printf("---------------------------\n");
	}
	return 0;
}

//注册功能
int sign_up(int cfd)
{
	struct communication snd_msg;
	char name[32] = "";
	char msg[128] = "";
	printf("请输入用户名>>>");
	scanf("%s", name);
	printf("请输入密码>>>");
	scanf("%s", msg);
	while (getchar() != 10);
	snd_msg.mode = 'S';
	strcpy(snd_msg.name, name);
	strcpy(snd_msg.msg, msg);
	//向服务器发送注册信息,由服务器确认是否有重复
	if (send(cfd, &snd_msg, sizeof(snd_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	//接收服务器的查询注册结果信息
	ssize_t res = 0;
	res = recv(cfd, &snd_msg, sizeof(snd_msg), 0); //阻塞方式接收,等价于read函数
	if (res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if (0 == res)
	{
		fprintf(stderr, "服务器下线\n");
		//break;
	}
	printf("[%s]: %s\n", snd_msg.name, snd_msg.msg);
	return 0;
}

//登录功能
int log_on(int cfd)
{
	struct communication snd_msg;
	char name[32] = "";
	char msg[128] = "";
	printf("请输入用户名>>>");
	scanf("%s", name);
	printf("请输入密码>>>");
	scanf("%s", msg);
	while (getchar() != 10);
	snd_msg.mode = 'L';
	strcpy(snd_msg.name, name);
	strcpy(snd_msg.msg, msg);
	//向服务器发送登录信息,由服务器确认是否存在,存在的话登录,密码不正确时提示密码错误
	if (send(cfd, &snd_msg, sizeof(snd_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	//接收服务器的登录返回信息
	ssize_t res = 0;
	res = recv(cfd, &snd_msg, sizeof(snd_msg), 0); //阻塞方式接收,等价于read函数
	if (res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if (0 == res)
	{
		fprintf(stderr, "服务器下线\n");
		//break;
	}
	printf("[%s]: %s\n", snd_msg.name, snd_msg.msg);
	//判断登录后是否成功,然后进行后续操作
	if (strcmp(snd_msg.msg, "登录成功") == 0)
	{
		function(cfd, snd_msg); //二级菜单的功能
		return 0;
	}
	return 0;
}

//查询单词
int look_word(int cfd, struct communication snd_msg)
{
	snd_msg.mode = 'C';
	char msg[128] = "";
	printf("请输入要查询的单词>>>");
	scanf("%s", msg);
	while (getchar() != 10);
	strcpy(snd_msg.msg, msg);
	//向服务器发送查询信息,由服务器返回查询的单词的含义
	if (send(cfd, &snd_msg, sizeof(snd_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	//接收服务器的登录返回信息
	ssize_t res = 0;
	res = recv(cfd, &snd_msg, sizeof(snd_msg), 0); //阻塞方式接收,等价于read函数
	if (res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if (0 == res)
	{
		fprintf(stderr, "服务器下线\n");
		//break;
	}
	printf("[%s]:查询单词及释义: %s\n",snd_msg.name, snd_msg.msg);
	return 0;
}

//查询历史记录
int look_history(int cfd, struct communication snd_msg)
{
	snd_msg.mode = 'H';  //查询历史记录
	//根据当前用户的用户名查询对应的历史记录
	//向服务器发送历史查询信息,由服务器返回历史记录
	if (send(cfd, &snd_msg, sizeof(snd_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	//接收服务器发送过来的历史记录信息
	ssize_t res = 0;
	res = recv(cfd, &snd_msg, sizeof(snd_msg), 0); //阻塞方式接收,等价于read函数
	if (res < 0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if (0 == res)
	{
		fprintf(stderr, "服务器下线\n");
		//break;
	}
	printf("[%s]用户下查询到的历史记录:\n", snd_msg.name);
	printf("%s\n", snd_msg.msg);
	return 0;
}

//返回上一级,退出登录
int log_out(int cfd, struct communication snd_msg)
{
	snd_msg.mode = 'E';  //当前用户退出登录信息
	//向服务器发送退出登录信息
	if (send(cfd, &snd_msg, sizeof(snd_msg), 0) < 0)
	{
		ERR_MSG("send");
		return -1;
	}
	return 0;
}

//二级菜单功能函数选择
int function(int cfd, struct communication snd_msg)
{
	char choose = 0;
	while (1)
	{
		menu(2);
		printf("请输入您需要的功能>>>");
		choose = getchar();
		while (getchar() != 10); //吸收垃圾字符
		switch (choose)
		{
		case '1':
			//查询单词
			look_word(cfd, snd_msg);
			break;
		case '2':
			//查询的历史记录
			look_history(cfd, snd_msg);
			break;
		case '3':
			//返回上一级
			log_out(cfd, snd_msg);
			return 0;
		default:
			printf("输入错误,请重新输入");
		}
	}
}

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

	//填充服务器的地址信息结构体---给connect函数使用,指定要连接的服务器
	//bind参数为struct sockaddr *addr,通用结构体指针,
	//真实的地址信息结构体根据地址族执行,AF_INET: man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;			 //必须填写AF_INET
	sin.sin_port = htons(PORT);			 //端口号的网络字节序,1024 - 49151
	sin.sin_addr.s_addr = inet_addr(IP); //IP地址的网络字节序,ifconfig可查看

	//连接服务器
	if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
	}
	printf("connect sucess \n");

	char buf[128] = "";
	ssize_t res = 0;
	struct communication snd_msg; //结构体格式的通信协议
	char choose = 0;

	while (1)
	{
		//菜单选择功能:注册,登录,退出
		//system("clear");
		menu(1);
		printf("请输入>>>");
		choose = getchar();
		while (getchar() != 10); //吸收垃圾字符

		switch (choose)
		{
		case '1':
			//注册功能
			sign_up(cfd);
			break;
		case '2':
			//登录
			log_on(cfd);
			break;
		case '3':
			//退出
			exit(0);
		default:
			printf("输入错误,请重新输入\n");
		}
		//printf("请输入任意字符清屏>>> ");
		// while (getchar() != 10);   //还留着注释,因为这里有一个故事
	}

	//关闭套接字文件描述符
	close(cfd);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值