C语言知识阶段性总结项目:电子词典

项目需求

  1. 使用TCP实现客户端和服务端通信
  2. 使用sqlite存放用户信息
  3. 客户端需要有登录、注册、查询单词、账号查询记录功能
  4. 服务器需要实时显示在线用户

解决方案

  1. 使用sqlite创建三个数据库,分别存放用户账号密码,单词表,用户查询记录
  2. 使用链表存放在线用户的信息,在子线程中循环遍历,达到实时显示在线用户的效果

主要的功能代码

头文件

#ifndef MSQLITE_H
#define MSQLITE_H
#include "Client_list.h"


//线程传参
typedef struct msg
{
	ClistPtr C;
	int cfd;
	struct sockaddr_in caddr;
} Msg_t;
 
//用户信息结构体            
typedef struct umsg             
{    
	char type;
	char id[20];                 
	char password[20];           
} Umsg_t;                          
 
 //查询单词的结构体        
 typedef struct Word              
 {                         
     char word[20];        
     char mean[50];
	 char time[20];
 } Word_t;   




//打开电子词典库
int Open_Dictdb(sqlite3* dictdb);

//打开用户数据库
int Open_Userdb(sqlite3* userdb);

//打开历史记录库
int Open_Histroy(sqlite3* histroy);

//导入电子词典
int Insert_Dict(sqlite3* dictdb);

//判断用户登入注册
int Connect_Event(sqlite3* userdb, sqlite3* histroy, Umsg_t usermsg, int cfd, ClistPtr C);

//用户登入函数
int User_Login(sqlite3* userdb, Umsg_t usermsg, int cfd, ClistPtr C);

//用户注册函数
int User_Register(sqlite3* userdb, Umsg_t usermsg, int cfd);

//计算文件大小
int Get_File_Line(char* file);

//单词查找函数
int Search_Word(Word_t word, int cfd, Umsg_t usermsg, sqlite3* dictdb, sqlite3* histroy);

//查看历史记录
int Show_History(sqlite3* histroy, Umsg_t usermsg, int cfd);


#endif

函数实现

#include 
#include 
#include 
#include 
#include 
#include 
#include "msqlite.h"




#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);

/*
	函数功能:打开词典数据库
	函数参数:词典数据库指针
	返回值:成功返回0 失败返回-1
*/
int Open_Dictdb(sqlite3* dictdb)
{
	int ret = -1;
	//打开单词数据库

	if(sqlite3_open("./dict.db", &dictdb) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return ret;
	}

	char sql[128] = "create table if not exists dict(word char, mean char);";
	char* errmsg = NULL;
	if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
		return ret;
	}



	printf("[%s] Dictdb open success\n", __DATE__);	

	ret = 0;
	return ret;
}

/*
	函数功能:打开用户数据库
	函数参数:用户数据库指针
	返回值:成功返回0 失败返回-1
*/
int Open_Userdb(sqlite3* userdb)
{
	int ret = -1;

	if(sqlite3_open("./user.db", &userdb) != SQLITE_OK)                                                
	{                                                                                                  
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);                                     
		return 0;                                                                                   
	}
	char sql[128] = "create table if not exists user(id char PRIMARY KEY, password char);";
	char* errmsg = NULL;
	if(sqlite3_exec(userdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
		return ret;
	}



	ret = 0;
	return ret;
}

/*
	函数功能:打开历史记录数据库
	函数参数:历史记录数据库指针
	返回值:成功返回0 失败返回-1
*/
int Open_Histroy(sqlite3* histroy)
{
	int ret = -1;

	if(sqlite3_open("./histroy.db", &histroy) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return ret;
	}

	ret = 0;
	return ret;
}




/*
	函数功能:将电子词典文本导入电子词典数据库
	函数参数:词典数据库指针
	返回值:成功返回0 失败返回-1
*/
int Insert_Dict(sqlite3* dictdb)
{

	if(sqlite3_open("./dict.db", &dictdb) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return -1;
	}

	char sql[128] = "create table if not exists dict(word char, mean char);";
	char* errmsg = NULL;
	if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
		return -1;
	}

	FILE* fp_r;
	char word[64] = "";
	char mean[64] = "";
	char buf[128] = "";
	int count = 0;

	if((fp_r = fopen("./dict.txt", "r")) == NULL)
	{
		LOG("fopen error");
		return -1;
	}

	//获取文件行数
	int size = Get_File_Line("./dict.txt");

	printf("词典正在录入。。。\n");
	while(1)
	{
		bzero(buf, sizeof(buf));
		bzero(mean, sizeof(mean));
		bzero(word, sizeof(word));

		if(size == count++)
			break;
		if(fgets(buf, sizeof(buf), fp_r) == NULL)
		{
			LOG("fgets error");
			return -1;
		}
		buf[strlen(buf)-1] = 0;

		//词,义分离的判断逻辑
		int flag = 0;
		for(int i = 0; i < strlen(buf); i++)
		{
			if(buf[i] == ' ' && 0 == flag)
			{
				strncpy(word, buf, i+1);
				word[strlen(word)-1] = 0;
				flag = 1;
			}

			if(buf[i] != ' ' && 1 == flag)
			{
				strcpy(mean, &buf[i]);
				break;
			}

		}

		char sql[128] = "";
		sprintf(sql, "insert into dict values (\"%s\", \"%s\");", word, mean);
		char* errmsg = NULL;
		if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n",__FILE__, __LINE__, errmsg);
			perror("sqlite3_exec");
			return -1;
		}

	}

	printf("词典录入完成\n");

	return 0;
}


/*
	函数功能:客户端连接事件
	函数参数:用户库指针,历史记录库指针,用户信息结构体,与客户端通信的文件描述符,用户链表头节点指针
	返回值:触发事件返回1 反之返回0
*/
int Connect_Event(sqlite3* userdb, sqlite3* histroy, Umsg_t usermsg, int cfd, ClistPtr C)
{

	if(usermsg.type == 'L')
	{
		return User_Login(userdb, usermsg, cfd, C);
	}
	else if(usermsg.type == 'R')
	{
		return User_Register(userdb, usermsg, cfd);
	}

	return 0;
}


/*
	函数功能:客户端登录事件
	函数参数:用户库指针,用户信息结构体,与客户端通信文件描述符,用户信息链表头指针
	返回值:成功返回1 失败返回-1
*/
int User_Login(sqlite3* userdb, Umsg_t usermsg, int cfd, ClistPtr C)
{
	int ask = -1;//应答标志

	if(sqlite3_open("./user.db", &userdb) != SQLITE_OK)                                                
	{                                                                                                  
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);                                     
		return -1;                                                                                   
	}
	char sql[128] = "create table if not exists user(id char, password char);";
	char* errmsg = NULL;
	if(sqlite3_exec(userdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
		return -1;
	}
	char cmp[128] = "";
	sprintf(cmp,"SELECT * FROM user WHERE id like \"%s\" AND password like \"%s\"",usermsg.id,usermsg.password);
	char ** pres = NULL;
	int row,column; 
	if(sqlite3_get_table(userdb, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_get_table:%s\n", __FILE__, __LINE__, errmsg);
		return -1;
	}

	ClistPtr p;
	//判断用户状态
	//重复登陆
	if((p = List_Search_User(C, usermsg.id)))
	{	
		ask = 5;
		if(send(cfd, &ask, sizeof(ask), 0) < 0)
		{				
			LOG("send");
			return -1;
		}
		printf("%s用户重复登录\n",usermsg.id);
		return -1;

	}
	//未登录
	else if(row == 1)
	{
		ask = 1;
		if(send(cfd, &ask, sizeof(ask), 0) < 0)
		{
			LOG("send");
			return -1;
		}
		printf("%s用户登入成功\n",usermsg.id);
		sqlite3_free_table(pres);
		pres = NULL;
		return 1;
	}
	//登录失败
	else
	{
		if(send(cfd, &ask, sizeof(ask), 0) < 0)
		{
			LOG("send");
			return -1;
		}
		return -1;
	}

	sqlite3_free_table(pres);
	pres = NULL;

	return -1;
}

/*
	函数功能:客户端注册事件
	函数参数:用户库指针,用户信息结构体,与客户端通信文件描述符
	返回值:成功返回1 失败返回-1
*/
int User_Register(sqlite3* userdb, Umsg_t usermsg, int cfd)
{
	if(sqlite3_open("./user.db", &userdb) != SQLITE_OK)                                                
	{                                                                                                  
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);                                     
		return -1;                                                                                   
	}
	char sql[128] = "create table if not exists user(id char PRIMARY KEY, password char);";
	char* errmsg = NULL;
	if(sqlite3_exec(userdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec\n", __FILE__, __LINE__);
		perror("");
		return -1;
	}

	int ask = -1;//应答标志
	char msg[128] = "";
	sprintf(msg, "insert into user values (\"%s\",\"%s\");", usermsg.id, usermsg.password);
	if(sqlite3_exec(userdb, msg, NULL, NULL, &errmsg) != SQLITE_OK)
	{	
		fprintf(stderr, "{%s:%d} sqlite3_exec:", __FILE__, __LINE__);
		perror("");
		if(send(cfd, &ask, sizeof(ask), 0) < 0)
		{
			LOG("send");
			return -1;
		}
		return -1;
	}
	/*
	char cmp[128] = "";
	sprintf(cmp,"SELECT * FROM user WHERE id like \"%s\" AND password like \"%s\";",usermsg.id,usermsg.password);
	char ** pres = NULL;
	int row,column; 
	if(sqlite3_get_table(userdb, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_get_table:%s\n", __FILE__, __LINE__, errmsg);
		return -1;
	}
	*/
	ask = 1;
	if(send(cfd, &ask, sizeof(ask), 0) < 0)
	{
		LOG("send");
		return -1;
	}
	printf("%s用户注册成功\n",usermsg.id);

	return 1;
}



/*
	函数功能:计算文件行数
	函数参数:文件路径
	返回值:成功返回文件行数 失败返回-1
*/
int Get_File_Line(char* file)
{
	int ret = -1;
	int line = 0;
	int ch;
	FILE * path;
	path = fopen(file, "r+");
	if (NULL == path)
	{
		LOG("open file error");
		return  ret; //文件大小不可能为负数
	}
	while((ch = fgetc(path)) != EOF)
	{
		if(ch == '\n')
		{
			line++;
		}
	}

	return (ret = line);
}


/*
	函数功能:服务器查询单词功能
	函数参数:单词结构体,通信文件描述符,用户信息结构体,单词库,历史记录库
	返回值:成功返回0 失败返回-1
*/
int Search_Word(Word_t word, int cfd, Umsg_t usermsg, sqlite3* dictdb, sqlite3* histroy)
{
	int ret = -1;
	if(sqlite3_open("./dict.db", &dictdb) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return ret;
	}

	char sql[128] = "create table if not exists dict(word char, mean char);";
	char* errmsg = NULL;
	if(sqlite3_exec(dictdb, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
		return ret;
	}

	if(sqlite3_open("./histroy.db", &histroy) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return ret;
	}

	bzero(sql, sizeof(sql));
	sprintf(sql, "create table if not exists %s(word char, mean char, time char);", usermsg.id);
	if(sqlite3_exec(histroy, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_exec:\n", __FILE__, __LINE__);
		perror("");
		return ret;
	}

	//查询单词和解释是否在数据库中
	char cmp[128] = "";

	sprintf(cmp,"SELECT * FROM dict WHERE word like \"%s\" OR mean like \" %s \"",word.word,word.mean);

	char ** pres = NULL;
	int row,column;  
	if(sqlite3_get_table(dictdb, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_get_table:%s\n", __FILE__, __LINE__, errmsg);
		return ret;
	}

	if(row == 0)
	{
		strcpy(word.mean, "未查询到此单词,请重试");
	}
	else
	{
		bzero(&word,sizeof(word));
		strcpy(word.word,pres[2]);
		strcpy(word.mean,pres[3]);

		time_t t;
		struct tm *info = NULL;
		char timenow[128] = "";
		t = time(NULL);
		info = localtime(&t);
		sprintf(timenow, "[%d-%02d-%02d] %02d:%02d:%02d",\
				info->tm_year+1900, \
				info->tm_mon+1, \
				info->tm_mday,\
				info->tm_hour, \
				info->tm_min, \
				info->tm_sec);

		//将单词存入历史记录表
		char sql[128] = "";
		bzero(sql,sizeof(sql));
		sprintf(sql, "insert into %s values(\"%s\",\"%s\",\"%s\");", usermsg.id, pres[2], pres[3], timenow);
		if(sqlite3_exec(histroy, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			fprintf(stderr, "{%s:%d} sqlite3_exec:%s\n", __FILE__, __LINE__, errmsg);
			return ret;
		}
	}

	//发送查询结果
	if(send(cfd, &word, sizeof(word), 0) < 0)
	{
		LOG("send");
		return ret;
	}

	ret = 0;

	return ret;
}


/*
	函数功能:服务器查询用户历史查询记录
	函数参数:历史记录库,用户信息结构体,通信文件描述符
	返回值:成功返回0 失败返回-1
*/
int Show_History(sqlite3* histroy, Umsg_t usermsg, int cfd)

{
	char cmp[128] = "";
	Word_t word;
	if(sqlite3_open("./histroy.db", &histroy) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_open failed\n", __FILE__, __LINE__);
		return -1;
	}
	sprintf(cmp,"SELECT * FROM %s;", usermsg.id);

	char* errmsg = NULL;
	char ** pres = NULL;
	int row,column;
	if(sqlite3_get_table(histroy, cmp, &pres, &row, &column, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "{%s:%d} sqlite3_get_table:%s\n", __FILE__, __LINE__, errmsg);
		return -1;
	}

	//遍历数据库中的指定表,分次发向客户端
	if(row > 0)
	{	//printf("__%s__ __ %d__ __%d__\n", __FILE__, __LINE__, row);
		for(int i = 3; i <= row*column; i+=3)
		{
			strcpy(word.word, pres[i]);
			strcpy(word.mean, pres[i+1]);
			strcpy(word.time, pres[i+2]);

			//printf("%s\t%s\t%s\n", pres[i], pres[i+1], pres[i+2]);

			if(send(cfd, &word, sizeof(word), 0) < 0)
			{
				LOG("send");
				return -1;
			}
		}
		strcpy(word.mean, "over");
		if(send(cfd, &word, sizeof(word), 0) < 0)
		{
			LOG("send");
			return -1;
		}
		return 1;
	}
	else
	{	
		strcpy(word.mean, "暂无历史记录");
		if(send(cfd, &word, sizeof(word), 0) < 0)
		{
			LOG("send");
			return -1;
		}
	}

	return 0;
}

链表头文件

#ifndef _CLIENT_LIST_H_
#define _CLIENT_LIST_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//存储用户信息的链表节点
typedef struct node
{
	union
	{
		struct sockaddr_in caddr;
		int len;
	};
	char id[20];
	struct node* next;
} Clist, *ClistPtr;

//创建头结点
ClistPtr List_Create();

//链表判空
int  List_Empty(ClistPtr C);

//创建普通节点
ClistPtr Node_Buy(struct sockaddr_in caddr, char* id);

//头插
void List_Insert_Head(ClistPtr C, struct sockaddr_in caddr, char* id);


//删除节点
void List_Delete(ClistPtr C, struct sockaddr_in caddr);


//销毁链表
void List_Free(ClistPtr *C);

//查找用户
ClistPtr List_Search_User(ClistPtr C, char* id);


#endif

链表函数实现

#include "Client_list.h"

#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);


/*
   函数功能:在堆区申请一个链表节点
   函数参数:void
   函数返回值:申请失败:NULL  申请成功:空间地址
*/
ClistPtr List_Create()
{
	//在堆区申请一个头结点类型
	ClistPtr C = (ClistPtr)malloc(sizeof(Clist));
	if(NULL == C)
	{
		LOG("malloc memory file!");
		return NULL;
	}

	//创建成功,对节点初始化
	C->len = 0; 	//初始化链表长度为0
	C->next = NULL; //链表上没有任何节点

	puts("list create compelet!");
	return C;
}

/*
   函数功能:判断链表是否为空
   函数参数:头结点地址
   函数返回值:传参异常:-1  为空:1
*/
int  List_Empty(ClistPtr C)
{
	//判断逻辑
	if(NULL == C)
	{
		LOG("malloc memory file!");
		return -1;
	}

	//判断指针域的内容
	return C->next == NULL && C->len == 0;
}

/*
   函数功能:创建普通节点
   函数参数:客户端地址信息结构体,用户ID
   函数返回值:申请失败:NULL   成功:节点地址
*/
ClistPtr Node_Buy(struct sockaddr_in caddr, char* id)

{
	//在堆区申请节点
	ClistPtr P = (ClistPtr)malloc(sizeof(Clist));
	if(NULL == P)
	{
		LOG("malloc memory file!");
		return NULL;
	}

	//节点申请成功,将数据封装进去
	P->caddr = caddr;
	strcpy(P->id, id);
	P->next = NULL;

	return P;
}

/*
   函数功能:链表头插
   函数参数:头结点地址,客户地址信息结构体,用户ID
   函数返回值:无
 */
void List_Insert_Head(ClistPtr C, struct sockaddr_in caddr, char* id)

{
	//判断逻辑
	if(NULL == C)
	{
		LOG("malloc memory file!");
		return ;
	}

	//创建节点并封装数据
	ClistPtr P = Node_Buy(caddr, id);
	if(NULL == P)
	{
		LOG("malloc memory file!");
		return ;
	}
	//插入逻辑
	P->next = C->next;
	C->next = P;

	//表格变化
	C->len++;
}

/*
   函数功能:节点删除
   函数参数:头结点地址,客户端地址信息结构体
   函数返回值:无
*/
void List_Delete(ClistPtr C, struct sockaddr_in caddr)
{
	if(NULL == C || List_Empty(C))
	{
		LOG("memory error!");
		return;
	}

	ClistPtr q = C;
	ClistPtr p = NULL;

	//查询要删除的节点
	while(q->next != NULL)
	{	
		if(!memcmp(&q->next->caddr, &caddr, sizeof(caddr)))
		{
			break;
		}
		q = q->next;
	}


	//删除逻辑
	p = q->next;
	q->next = q->next->next;
	p->next = NULL;
	free(p);
	p = NULL;

	C->len--;
}


/*
   函数功能:链表销毁
   函数参数:头结点指针地址
   函数返回值:无
*/
void List_Free(ClistPtr *C)
{
	//判断逻辑
	if(NULL == C || NULL == *C)
	{
		LOG("memory error!");
		return;
	}

	ClistPtr q = *C;

	while(*C != NULL)
	{
		q = q->next;
		free(*C);
		*C = q;
	}

	puts("list free");
}

/*
   函数功能:查询用户节点
   函数参数:头结点地址,用户ID
   函数返回值:成功返回用户节点地址,失败返回NULL
*/
ClistPtr List_Search_User(ClistPtr C, char* id)
{
	ClistPtr q = C->next;
	for(int i=1; i<=C->len; i++)
	{
		if(strcmp(q->id,id))
		{
			q = q ->next;
		}
		else
		{
			return q;
		}
	}

	return NULL;
}

客户端功能头文件

#ifndef __CLIENT_H__
#define __CLIENT_H__
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "server.h"

 
//登入注册界面
int Menu(int client);
 
//向服务器发送用户登入申请
int Login(int client);
 
//向服务器发送用户注册申请
int Register(int client);

//查询单词功能
int Search(int client);

//查询历史记录功能
int History(int client);

 
#endif

客户端功能实现

#include "client.h"
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);
Umsg_t usermsg;


//登录注册界面
int Menu(int client)
{
	int ret;
		while(1)
		{	
			system("clear");
				printf("-----------1.登入------------\n");
				printf("-----------2.注册------------\n");
				printf("-----------3.退出------------\n");
				
				
				char choose; //存储选择的序号
			printf("请选择:");
				scanf("%c",&choose);
				while(getchar() != 10);
					
						switch(choose)
						{
						case '1':
								 ret = Login(client);
									 return ret;
									 break;
						case '2':
								 ret = Register(client);
									 return ret;
									 break;
						case '3':
								 return 1;
						default:
								printf("输入错误,请重新输入\n");
						}
			printf("输入任意字符清屏>>>");
				while(getchar() != 10);
					
		}	
	return ret;
}


/*
函数功能:客户端登录
函数参数:客户端文件描述符
返回值:登录成功返回0,失败返回-1
*/
int Login(int client)
{
	while(1)
	{
		printf("------登录------\n");
			//填入账号密码数据
			usermsg.type = 'L';
			printf("请输入账号>>>");
			scanf("%s",usermsg.id);
			while(getchar() != 10);
				printf("请输入密码>>>");
					scanf("%s",usermsg.password);
					while(getchar() != 10);
						
							system("clear");
							printf("登陆中。。。");
							//发送给服务器
							if(send(client, &usermsg , sizeof(usermsg), 0) < 0)
							{
								LOG("send");
									return -1;
							}
		
			//接受服务器发来的确认信息	
			int flag;
			ssize_t res;
			res = recv(client, &flag, sizeof(flag), 0);
			//printf("{%s:%d} flag = %d\n", __FILE__, __LINE__, flag);
			if(res < 0)
			{
				LOG("recv");
					return -1;
			}
			else if(0 == res)
			{
				return -1;
			}
		
			system("clear");
			
			//判断服务器发来的确认信息 1为成功,-1为失败(数据错误)
			if(flag == 5)
			{
				printf("重复登录\n");
			}
			else if(flag == 1)
			{
				printf("登入成功\n");
					break;
			}
			else if(flag == -1)
			{
				printf("用户账号密码错误\n");
			}
		
	}
	return 0;
}

/*
函数功能:客户端注册
函数参数:客户端文件描述符
返回值:注册成功返回0,失败返回-1
*/
int Register(int client)
{
	printf("------注册------\n");
		while(1)
		{
			//填入要注册账号密码数据
			usermsg.type = 'R';
				printf("请输入注册账号:");
				scanf("%s",usermsg.id);
				while(getchar() != 10);
					printf("请输入注册密码:");
						scanf("%s",usermsg.password);
						while(getchar() != 10);
							
								//发送给服务器
								if(send(client, &usermsg , sizeof(usermsg), 0) < 0)
								{
									LOG("send");
										return -1;
								}
			
				//接受服务器发来的确认信息	
				int flag;
				ssize_t res;
				res = recv(client, &flag, sizeof(flag), 0);
				if(res < 0)
				{
					LOG("recv");
						return -1;
				}
				else if(0 == res)
				{
					printf("server is off-line\n");
						return -1;
				}
			
				//判断服务器发来的确认信息 1为成功,-1为失败(数据错误)
				if(flag == 1)
				{
					printf("注册成功\n");
						break;
				}
				else if(flag < 0)
				{
					printf("注册失败,重复注册\n");
				}
		}
	return 0;
}

/*
函数功能:客户端单词查询
函数参数:客户端文件描述符
返回值:查询成功返回0,失败返回-1
*/
int Search(int client)
{
	//查询单词操作
	char buf[128] = "";
	ssize_t res = 0;
	Word_t words;
		usermsg.type = 'S';
		//发送给服务器
		if(send(client, &usermsg , sizeof(usermsg), 0) < 0)
		{
			LOG("send");
				return -1;
		}
	system("clear");
		
		
		while(1)
		{		
			//发送功能
			bzero(&words,sizeof(words));
			bzero(buf, sizeof(buf));
	
			printf("请输入要查询的单词>>>");
				fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
			strcpy(words.word,buf); 
				
				if(send(client, &words, sizeof(words), 0) < 0)
				{
					LOG("send");
					return -1;
				}
			system("clear");

			//接收功能 
			bzero(&words, sizeof(words));
			res = recv(client, &words, sizeof(words), 0);
				if(res < 0)
				{
					LOG("recv");
					return -1;
				}
				else if(0 == res)
				{
					printf("服务器已掉线\n");
					break;
				}
			
				printf("%s\t%s\n", words.word, words.mean);
		}
	
		return 0;
}

/*
函数功能:客户端历史记录查询
函数参数:客户端文件描述符
返回值:查询成功返回0,失败返回-1
*/
int History(int client)
{
	Word_t word;
	usermsg.type = 'H';

		//命令发送
		if(send(client, &usermsg , sizeof(usermsg), 0) < 0)
		{
			LOG("send");
				return -1;
		}
	system("clear");

		//接收功能
		while(1)
		{
			bzero(&word, sizeof(word));
				int res = recv(client, &word, sizeof(word), 0);
				if(res < 0)
				{
					LOG("recv");
					return -1;
				}
				else if(0 == res)
				{
					printf("服务器已掉线\n");
					return -1;
				}
			if(!strcmp(word.mean, "over"))break;
				printf("%s\t%s\t\t%s\n", word.word, word.mean, word.time);
		}
	printf("输入任意字符清屏>>>");
		while(getchar() != 10);
			system("clear");
				return 0;
}

客户端main函数

#include "client.h"
#include 

#define PORT 8888                                    
#define IP  "192.168.114.73"  
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);
int main(int argc, const char *argv[])
{

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



	//填充要连接的服务器的地址信息结构体
	struct sockaddr_in saddr;
	saddr.sin_family 		= AF_INET;
	saddr.sin_port 		= htons(PORT);
	saddr.sin_addr.s_addr = inet_addr(IP);

	//连接服务器 connect
	if(connect(cfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
	{
		LOG("connect");
		return -1;
	}

	int ret = 0;
	while(1)
	{
		ret = Menu(cfd);

		if(ret== 0)
		{
			printf("connect success\n");
		}
		else if(ret == -1)
		{
			printf("服务器掉线\n");
			return -1;
		}
		else
		{
			return -1;
		}

		while(1)
		{
			system("clear");
			printf("-----------1.查单词------------\n");
			printf("-----------2.历史记录----------\n");


			char choose; //存储选择的序号
			printf("请选择:");
			scanf("%c",&choose);
			while(getchar() != 10);

			switch(choose)
			{
			case '1':
				Search(cfd);
				break;
			case '2':
				History(cfd);
				break;
			default:
				printf("输入错误,请重新输入\n");
				break;
			}
			printf("输入任意字符清屏>>>");
			while(getchar() != 10);

		}	

	}	

	//关闭套接字
	close(cfd);

	return 0;
}

服务器功能函数

#ifndef SERVER_H
#define SERVER_H
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "msqlite.h"
#include "Client_list.h"

#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);
 
                     
                           
 
 
//循环打印已有客户端的线程
void* loop_printf(void* arg);

//遍历链表
void list_show(ClistPtr C);
 

#endif

服务器函数实现

#include "server.h"


//循环打印已有客户端的线程                                                                             
void* loop_printf(void* arg)                                                                           
{                                                                                                      
	ClistPtr C = arg;                                                                                 

	pthread_detach(pthread_self());                                                                    
	while(1)                                                                                           
	{                                                                                                  
		system("clear");                                                                               
		list_show(C);                                                                                  
		sleep(3);                                                                                      
	}                                                                                                  

	pthread_exit(NULL);                                                                                
}                                                                                                      

//遍历链表,终端输出客户端信息
void list_show(ClistPtr C)
{
	time_t t;
	struct tm *info = NULL;
	char timenow[128] = "";
	t = time(NULL);
	info = localtime(&t);
	sprintf(timenow, "[%d-%02d-%02d] %02d:%02d:%02d",\
			info->tm_year+1900, \
			info->tm_mon+1, \
			info->tm_mday,\
			info->tm_hour, \
			info->tm_min, \
			info->tm_sec);

	//判断逻辑
	if( NULL == C || List_Empty(C) )
	{
		printf("时间: %s\t暂无用户\n", timenow);
		return;
	}

	//遍历逻辑
	printf("时间: %s\t在线用户个数%d:\n",timenow, C->len);
	ClistPtr q = C->next;
	while(q != NULL)
	{
		printf("IP: %s\t|\tPort: %d\t|\tId: %s\n", inet_ntoa(q->caddr.sin_addr), ntohs(q->caddr.sin_port), q->id);
		q = q->next;
	}
	printf("\n");
}


服务器主函数

#include "server.h"

#define PORT 8888 				//1024~49151
#define IP 	"192.168.114.73" 	//本机IP,用ifconfig查看

#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s);

//回调函数                                                                                         
void* Rcv_Msg(void* arg)                                         
{                                                                                                      
	//分离分支线程                                                                                     
	pthread_detach(pthread_self());                                                                    
	sqlite3* userdb;
	sqlite3* histroy;
	sqlite3* dictdb;
	int cfd = (*(Msg_t*)arg).cfd;                                                             
	struct sockaddr_in caddr = (*(Msg_t*)arg).caddr;                                                                                                                                                        
	ClistPtr C = (*(Msg_t*)arg).C;                                                               

	Umsg_t usermsg;                                                                           


	while(1)
	{
		ssize_t res;
		bzero(&usermsg, sizeof(usermsg));
		res = recv(cfd, &usermsg, sizeof(usermsg), 0);                                                                     
		if(res < 0)                                                                                                 
		{                                                                                                           
			LOG("recv");                                                                                        
			return NULL;                                                                                       
		}                                                                                                           
		else if(0 == res)                                                                                           
		{                                                                                                           
			printf("[%s] [%s / %d] client: %d 客户端离线\n", __DATE__, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), cfd);
			close(cfd);
			pthread_exit(NULL);
			return NULL;
		}                                                                                                           

		if( ( Connect_Event(userdb, histroy, usermsg, cfd, C)) == 1)
		{
			break;	
		}
	}

	List_Insert_Head(C , caddr, usermsg.id);

	while(1)
	{
		ssize_t res;
		bzero(&usermsg, sizeof(usermsg));
		res = recv(cfd, &usermsg, sizeof(usermsg), 0);
		if(res < 0)                                                                                                 
		{                                                                                                           
			LOG("recv");                                                                                        
			return NULL;                                                                                       
		}                                                                                                           
		else if(0 == res)                                                                                           
		{                                                                                                           
			printf("[%s] [%s / %d] client: %d 客户端离线\n", __DATE__, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), cfd);
			List_Delete(C, caddr);
			close(cfd);
			pthread_exit(NULL);
			return NULL;
		}

		if(usermsg.type == 'H')
		{
			Show_History(histroy, usermsg, cfd);
			continue;
		}
		else if(usermsg.type == 'S')
		{
			Word_t words;
			char buf[128] = "";
			while(1)
			{
				bzero(&words,sizeof(words));
				bzero(buf, sizeof(buf));
				//循环接收 
				res = recv(cfd, &words, sizeof(words), 0);
				if(res < 0)
				{
					LOG("recv");
					break;
				}
				else if(0 == res)
				{
					printf("[%s : %d] newfd = %d 客户端掉线\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), cfd);
					List_Delete(C, caddr);
					close(cfd);
					pthread_exit(NULL);
					return NULL;
				}

				Search_Word(words, cfd, usermsg, dictdb, histroy);

			}
		}

	}



}


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

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

	//填充地址信息结构体,真实的地址信息结构体与协议族相关
	//AF_INET,所以详情请看man 7 ip
	struct sockaddr_in saddr;
	saddr.sin_family 		= AF_INET;
	saddr.sin_port 		= htons(PORT); 	//网络字节序的端口号
	saddr.sin_addr.s_addr = inet_addr(IP); 	//网络字节序的IP地址

	//将地址信息结构体绑定到套接字上
	if(bind(sfd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0)
	{
		LOG("bind");
		return -1;
	}
	printf("bind success\n");

	//将套接字设置为被动监听状态,让内核去监听是否有客户端连接;
	if(listen(sfd, 10) < 0)
	{
		LOG("listen");
		return -1;
	}
	printf("listen success\n");
	sqlite3* dictdb;
	sqlite3* userdb;
	sqlite3* histroy;
	Open_Dictdb(dictdb);
	//Insert_Dict(dictdb);
	Open_Userdb(userdb);
	Open_Histroy(histroy);



	//创建存储客户端网络结构体链表
	ClistPtr C = List_Create();
	if(NULL == C)
	{
		return -1;
	}
	//定义存储客户端网络结构体
	struct sockaddr_in caddr;
	socklen_t addrlen = sizeof(caddr);

	int newfd = 0;
	pthread_t tid;
	Msg_t msg;


	//循环打印已有客户端的线程

	if(pthread_create(&tid, NULL, loop_printf, (void*)C) != 0)
	{
		LOG("pthread_create");
		return -1;
	}

	while(1)
	{
		//主线程主要负责连接
		newfd = accept(sfd, (struct sockaddr*)&caddr, &addrlen);
		if(newfd < 0)
		{
			LOG("accept");
			return -1;
		}

		//网络字节序的IP-->点分十进制  网络字节序的port--->本机字节序
		printf("[%s : %d] newfd = %d 客户端连入\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port),newfd);

		msg.cfd = newfd;
		msg.caddr = caddr;
		msg.C = C;


		//一旦连接成功后,创建一个分支线程用户与客户端交互;
		if(pthread_create(&tid, NULL, Rcv_Msg, (void*)&msg) != 0)
		{
			LOG("pthread_create");
			return -1;
		}
	}

	close(sfd);
	List_Free(&C);
	return 0;
}

你可能感兴趣的:(c语言,开发语言)