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

2023-09-17 15:49:30

项目需求

  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 <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <sqlite3.h>
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>

//存储用户信息的链表节点
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 <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#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 <sqlite3.h>

#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 <signal.h>
#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#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;
}

更多推荐

【PyTorch 攻略 (4/7)】张量和梯度函数

一、说明W在训练神经网络时,最常用的算法是反向传播。在该算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整。损失函数计算神经网络产生的预期输出和实际输出之间的差异。目标是获得尽可能接近零的损失函数的结果。反向传播算法通过神经网络向后遍历,以调整权重和偏差以重新训练模型。这种随着时间的推移重新训练模型的来回

Word2Vec的原理是什么,如何用训练Word2Vec

Word2Vec是一种基于神经网络的词向量生成模型,通过训练预测上下文单词或中心单词来生成词向量。它包含两种不同的架构:跳字模型(Skip-gram)和连续词袋模型(ContinuousBag-of-Words,CBOW),它们在训练方式和结果表现上略有不同。1跳字模型(Skip-gram):在跳字模型中,模型的目标是

ES7新特性深度解析:提升JavaScript开发效率的利器

前言ES7(ECMAScript2016)是JavaScript的最新版本,引入了一些强大的新特性,旨在提升开发者的工作效率。本篇博客将深度解析ES7的一些重要特性,并且以不超过50%的代码比例展示其用法。包含属性初始化器的类(Class)定义ES7允许我们在类的定义中直接初始化属性,而不需要在constructor中

Docker镜像:构建、推送和创建多个容器实例

文章目录步骤1:构建自定义Docker镜像步骤2:推送和拉取Docker镜像步骤3:创建多个容器实例结论🎈个人主页:程序员小侯🎐CSDN新晋作者🎉欢迎👍点赞✍评论⭐收藏✨收录专栏:云计算✨文章内容:Docker镜像🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,大家一起学习交流!🤗Docke

PX4自动控制中常用的几个话题

订阅话题1、/mavros/state订阅/发布:订阅功能:订阅mavros的一些状态数据,如连接状态、是否解锁、当前无人机模式数据类型:mavros_msgs/StatestringMODE_PX4_MANUAL=MANUALstringMODE_PX4_ACRO=ACROstringMODE_PX4_ALTITUD

处理SQLSyntaxErrorException异常:数据库表 ‘books‘ 不存在;

目录背景介绍我的问题中的解决方法通用方法背景介绍今天遇见了这个问题,解决后发出来分享一下Java应用程序中的SQLSyntaxErrorException:表'bookmanagement.books'不存在问题解决解决MySQL错误:无法找到表'bookmanagement.booksjava.sql.SQLSynt

从数据、产品、管理的视角探讨MES管理系统

MES生产管理系统在企业生产中扮演着关键角色,它集成了生产流程、数据应用、管理功能等多方面要素,为企业提供全面的生产运营支持。本文将从数据的视角聊聊MES管理系统,介绍其在数据管理、分析和利用方面的应用;从产品的视角,剖析其在生产过程管理、质量控制和追溯能力等方面的功能;最后从管理的视角,阐述其在生产调度、资源管理和决

KVCache原理简述

在GPT的推理过程中,它根据完整的提问和回答的已生成部分,来生测下一个词(的概率)。例如,我们的提问是【天王盖地虎,】,回答是【宝塔镇河妖。】。那么第一次,GPT根据【天王盖地虎,】生成【宝】,之后根据【天王盖地虎,宝】生成【塔】,以此类推,直到碰上终止符。这里面提问【天王盖地虎,】的QKV实际上重复计算了很多遍。由于

学了1个月机器学习的总结

书实在是厚,看不下去,还是看视频容易接受。总结:入门应该从如何把点拟合成一条线开始。先从统计学里的方差开始,扩展最小二乘法,引出线性回归。然后是逻辑回归,引出机器学习核心——求代价函数最小值。进而引出正则、学习率、过拟合欠拟合、偏差方差、准确率召回率、训练集验证集测试集等机器学习基础。进而扩展知识面,从线性到非线性,神

一、八大排序(sort)

文章目录一、时间复杂度(一)定义:常数操作二、空间复杂度(一)定义:三、排序(一)选择排序1.定义2.代码3.特性(二)冒泡排序1.定义2.代码3.特性(三)插入排序1.定义2.代码3.特性(四)归并排序1.定义2.代码3.特性(五)快速排序(六)堆排序(七)基数排序(八)计数排序一、时间复杂度(一)定义:常数操作与数

口袋参谋:淘宝生意参谋指数,如何一键转换成真实数值?

在淘宝天猫上,我们是无法直接清楚的,了解竞品真实数据,因为平台为了保护商家的权益,将真实数值全部隐藏起来了,也就是变成了我们能看到的虚拟指数。那我们在生意参谋中,从市场排行、市场大盘等地方,看到的交易额、支付人数、转化率、收藏人数、加购人数等数据都是指数,而不是实际值。这样的话,我们在做竞品分析时,基本上就失去参考价值

热文推荐