L8Linux应用开发综合实战-在线词典项目(day1、2、3)

目录

一、在线词典项目介绍及框架搭建

一、有道词典流程分析及本项目在线词典介绍

 1、有道词典功能分析

2、项目流程 

二、 在线词典项目演示

三、流程示意图分析 

1、客户端

 2、服务器

 四、客户端代码框架搭建

五、服务器端代码框架搭建

 模板结构代码

创建数据库

 演示查看是否正常

 5.1:注册模块

5.2:登录模块

 5.3.查询模块

5.4 历史记录查询模块

 作业

第一题:

第二题: 我不会


一、在线词典项目介绍及框架搭建

一、有道词典流程分析及本项目在线词典介绍

 1、有道词典功能分析

Ø在线英英词典
Ø 项目功能描述
Ø 用户注册和登录验证
Ø 服务器端将用户信息和历史记录保存在数据库中。客户端输入用户名和密码,服务器端在数据库中查找、匹配,返回结果
Ø 单词在线翻译
Ø 根据客户端输入的单词在字典文件中搜索
Ø 历史记录查询
Ø 项目分析

2、项目流程 

Ø 项目流程
Ø 定义数据库中表的结构
Ø 定义消息结构体
Ø 分析服务器端和客户端流程
Ø 编码 实现

二、 在线词典项目演示

三、流程示意图分析 

1、客户端

 2、服务器

 四、客户端代码框架搭建

编译错误处理: warning: implicit declaration of function ‘close’ [-Wimplicit-function-declaration]

原来linux下面open和close是来源于不同的头文件:include <fcntl.h> // for open
                                                                                 include <unistd.h> // for close

网络信息结构体:

 模板结构代码:

client.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;



int  do_register(int sockfd, MSG *msg)
{
	printf("register ...\n");
	return 1;
}

int do_login(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 1;
}

int do_query(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 1;
}

int do_history(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 0;
}

	// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG  msg;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

		//连接服务器
		if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
		{
			perror("fail to connect");
			return -1;
		}

	//一级菜单
	while(1)
	{
		printf("***************************************************\n");
		printf("* 1.register          2.login           3.quit    *\n");
		printf("***************************************************\n");
		printf("please choose:");
		scanf("%d",&n);

		getchar();

		switch(n)
		{
		case 1:
			do_register(sockfd, &msg);
			break;
		case 2:
			if(do_login(sockfd, &msg) == 1)
			{
				goto next;
			}
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}

	}

	//二级菜单
next:
	while(1)
	{
		printf("*****************************************************\n");
		printf("* 1.query_word   2.history_record   3.quit          *\n");
		printf("*****************************************************\n");
		printf("Please choose:");
		scanf("%d", &n);
		getchar();

		switch(n)
		{
		case 1:
			do_query(sockfd, &msg);
			break;
		case 2:
			do_history(sockfd, &msg);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default :
			printf("Invalid data cmd.\n");
		}

	}

	return 0;
}

五、服务器端代码框架搭建

 模板结构代码

server.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include<sqlite3.h>
#include<signal.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);


void  do_register(int sockfd, MSG *msg,sqlite3 *db);


int do_login(int sockfd, MSG *msg,sqlite3 *db);

int do_query(int sockfd, MSG *msg,sqlite3 *db);

int do_history(int sockfd, MSG *msg,sqlite3 *db);


// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG msg;
	sqlite3 *db;
	int acceptfd;
	pid_t pid;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//打开数据库
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open DATABASE success\n");
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

	//绑定(建立套接字与地址联系)
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("fail to bind");
		return -1;
	}

	//将套接字设为监听模式
	if(listen(sockfd,5) < 0)//监听个数5
	{
		printf("fail to listen");
		return -1;
	}

	//处理僵尸进程
	signal(SIGCHLD,SIG_IGN);

	while(1)
	{
		if((acceptfd = accept(sockfd,NULL,NULL)) < 0)
		{
			perror("fail to accept");
			return -1;
		}
		//如果成功了,应创建父子进程,父进程永远接收客户端请求,子进程处理请求的具体内容
		if((pid = fork()) < 0)
		{
			perror("fail to fork");
			return -1;
		}
		else if(pid == 0)
		{
			//处理客户端具体的消息(子进程)
			close(sockfd);//监听套接字不用了,关闭
			do_client(acceptfd,db);

		}
		else
		{
			//接收客户端的请求(父进程)
			close(acceptfd);//接收套接字不用了,关闭
		}
	}


	return 0;
}


int do_client(int acceptfd,sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd,&msg,sizeof(msg),0) > 0)//接收成功
	{
			printf("type:%d\n", msg.type);
			switch(msg.type)
			{
			case R:
				do_register(acceptfd, &msg, db);
				break;
			case L:
				do_login(acceptfd, &msg, db);
				break;
			case Q:
				do_query(acceptfd, &msg, db);
				break;
			case H:
				do_history(acceptfd, &msg, db);
				break;
			default:
				printf("Invalid data msg.\n");
			}

		}

		printf("client exit.\n");//客户端退出就会跳出while循环
		close(acceptfd);
		exit(0);

		return 0;
	}


void  do_register(int sockfd, MSG *msg, sqlite3 *db)
{
	printf("register ...\n");
}

int do_login(int sockfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 1;
}

int do_query(int sockfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 1;
}

int do_history(int sockfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 0;
}

创建数据库

create table usr(name text primary key,pass text);             //创建用户表
create table record(name text,date text,word text);            // 创建记录表

 演示查看是否正常

 

 ps axj 查看有无僵尸进程

 没有僵尸进程

 5.1:注册模块

client.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;


int  do_register(int sockfd, MSG *msg)
{
	msg->type = R;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)//(msg已经是指针,不用取地址)是大写MSG不是小写,小写是地址(永远4字节)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	// ok !  or  usr alread exist.
	printf("%s\n", msg->data);

	return 0;
}

int do_login(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 1;
}

int do_query(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 1;
}

int do_history(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 0;
}

	// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG  msg;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

		//连接服务器
		if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
		{
			perror("fail to connect");
			return -1;
		}

	//一级菜单
	while(1)
	{
		printf("***************************************************\n");
		printf("* 1.register          2.login           3.quit    *\n");
		printf("***************************************************\n");
		printf("please choose:");
		scanf("%d",&n);

		getchar();

		switch(n)
		{
		case 1:
			do_register(sockfd, &msg);
			break;
		case 2:
			if(do_login(sockfd, &msg) == 1)
			{
				goto next;
			}
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}

	}

	//二级菜单
next:
	while(1)
	{
		printf("*****************************************************\n");
		printf("* 1.query_word   2.history_record   3.quit          *\n");
		printf("*****************************************************\n");
		printf("Please choose:");
		scanf("%d", &n);
		getchar();

		switch(n)
		{
		case 1:
			do_query(sockfd, &msg);
			break;
		case 2:
			do_history(sockfd, &msg);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default :
			printf("Invalid data cmd.\n");
		}

	}

	return 0;
}

server.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include<sqlite3.h>
#include<signal.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);


void  do_register(int acceptfd, MSG *msg,sqlite3 *db);


int do_login(int acceptfd, MSG *msg,sqlite3 *db);

int do_query(int acceptfd, MSG *msg,sqlite3 *db);

int do_history(int acceptfd, MSG *msg,sqlite3 *db);


// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG msg;
	sqlite3 *db;
	int acceptfd;
	pid_t pid;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//打开数据库
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open DATABASE success\n");
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

	//绑定(建立套接字与地址联系)
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("fail to bind");
		return -1;
	}

	//将套接字设为监听模式
	if(listen(sockfd,5) < 0)//监听个数5
	{
		printf("fail to listen");
		return -1;
	}

	//处理僵尸进程
	signal(SIGCHLD,SIG_IGN);

	while(1)
	{
		if((acceptfd = accept(sockfd,NULL,NULL)) < 0)
		{
			perror("fail to accept");
			return -1;
		}
		//如果成功了,应创建父子进程,父进程永远接收客户端请求,子进程处理请求的具体内容
		if((pid = fork()) < 0)
		{
			perror("fail to fork");
			return -1;
		}
		else if(pid == 0)
		{
			//处理客户端具体的消息(子进程)
			close(sockfd);//监听套接字不用了,关闭
			do_client(acceptfd,db);

		}
		else
		{
			//接收客户端的请求(父进程)
			close(acceptfd);//接收套接字不用了,关闭
		}
	}


	return 0;
}


int do_client(int acceptfd,sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd,&msg,sizeof(msg),0) > 0)//接收成功
	{
			printf("type:%d\n", msg.type);//打印消息类型
			switch(msg.type)
			{
			case R:
				do_register(acceptfd, &msg, db);
				break;
			case L:
				do_login(acceptfd, &msg, db);
				break;
			case Q:
				do_query(acceptfd, &msg, db);
				break;
			case H:
				do_history(acceptfd, &msg, db);
				break;
			default:
				printf("Invalid data msg.\n");
			}

		}

		printf("client exit.\n");//客户端退出就会跳出while循环
		close(acceptfd);
		exit(0);

		return 0;
	}


void  do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char * errmsg;
	char sql[512];//原本视频中是[128],会有警告

	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
	printf("%s\n", sql);

	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		strcpy(msg->data, "usr name already exist.");
	}
	else
	{
		printf("client  register ok!\n");
		strcpy(msg->data, "OK!");
	}

	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		return ;
	}

	return ;
}

int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 1;
}

int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 1;
}

int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 0;
}

运行结果:

 遇到的问题:

server.c: In function ‘do_register’:
server.c:172:52: warning: ‘%s’ directive writing up to 255 bytes into a region of size between 198 and 229 [-Wformat-overflow=]
  172 |         sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
      |                                                    ^~
server.c:172:9: note: ‘sprintf’ output between 30 and 316 bytes into a destination of size 256
  172 |         sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如何解决:把sql[128]改成了sql[512]

5.2:登录模块

 client.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;


int  do_register(int sockfd, MSG *msg)
{
	msg->type = R;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)//(msg已经是指针,不用取地址)是大写MSG不是小写,小写是地址(永远4字节)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	// ok !  or  usr alread exist.
	printf("%s\n", msg->data);

	return 0;
}

int do_login(int sockfd, MSG *msg)
{
	msg->type = L;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	if(strncmp(msg->data, "OK", 3) == 0)//看看是否为OK,比较3位因为还有\0
	{
		printf("Login ok!\n");
		return 1;
	}
	else 
	{
		printf("%s\n", msg->data);
	}

	return 0;
}

int do_query(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 1;
}

int do_history(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 0;
}

	// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG  msg;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

		//连接服务器
		if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
		{
			perror("fail to connect");
			return -1;
		}

	//一级菜单
	while(1)
	{
		printf("***************************************************\n");
		printf("* 1.register          2.login           3.quit    *\n");
		printf("***************************************************\n");
		printf("please choose:");
		scanf("%d",&n);

		getchar();

		switch(n)
		{
		case 1:
			do_register(sockfd, &msg);
			break;
		case 2:
			if(do_login(sockfd, &msg) == 1)
			{
				goto next;
			}
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}

	}

	//二级菜单
next:
	while(1)
	{
		printf("*****************************************************\n");
		printf("* 1.query_word   2.history_record   3.quit          *\n");
		printf("*****************************************************\n");
		printf("Please choose:");
		scanf("%d", &n);
		getchar();

		switch(n)
		{
		case 1:
			do_query(sockfd, &msg);
			break;
		case 2:
			do_history(sockfd, &msg);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default :
			printf("Invalid data cmd.\n");
		}

	}

	return 0;
}

server.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include<sqlite3.h>
#include<signal.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);


void  do_register(int acceptfd, MSG *msg,sqlite3 *db);


int do_login(int acceptfd, MSG *msg,sqlite3 *db);

int do_query(int acceptfd, MSG *msg,sqlite3 *db);

int do_history(int acceptfd, MSG *msg,sqlite3 *db);


// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG msg;
	sqlite3 *db;
	int acceptfd;
	pid_t pid;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//打开数据库
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open DATABASE success\n");
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

	//绑定(建立套接字与地址联系)
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("fail to bind");
		return -1;
	}

	//将套接字设为监听模式
	if(listen(sockfd,5) < 0)//监听个数5
	{
		printf("fail to listen");
		return -1;
	}

	//处理僵尸进程
	signal(SIGCHLD,SIG_IGN);

	while(1)
	{
		if((acceptfd = accept(sockfd,NULL,NULL)) < 0)
		{
			perror("fail to accept");
			return -1;
		}
		//如果成功了,应创建父子进程,父进程永远接收客户端请求,子进程处理请求的具体内容
		if((pid = fork()) < 0)
		{
			perror("fail to fork");
			return -1;
		}
		else if(pid == 0)
		{
			//处理客户端具体的消息(子进程)
			close(sockfd);//监听套接字不用了,关闭
			do_client(acceptfd,db);

		}
		else
		{
			//接收客户端的请求(父进程)
			close(acceptfd);//接收套接字不用了,关闭
		}
	}


	return 0;
}


int do_client(int acceptfd,sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd,&msg,sizeof(msg),0) > 0)//接收成功
	{
			printf("type:%d\n", msg.type);//打印消息类型
			switch(msg.type)
			{
			case R:
				do_register(acceptfd, &msg, db);
				break;
			case L:
				do_login(acceptfd, &msg, db);
				break;
			case Q:
				do_query(acceptfd, &msg, db);
				break;
			case H:
				do_history(acceptfd, &msg, db);
				break;
			default:
				printf("Invalid data msg.\n");
			}

		}

		printf("client exit.\n");//客户端退出就会跳出while循环
		close(acceptfd);
		exit(0);

		return 0;
	}


void  do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char * errmsg;
	char sql[512];

	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
	printf("%s\n", sql);

	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		strcpy(msg->data, "usr name already exist.");
	}
	else
	{
		printf("client  register ok!\n");
		strcpy(msg->data, "OK!");
	}

	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		return ;
	}

	return ;
}

int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sql[512] = {};//128太小会警告
	char *errmsg;
	int nrow;
	int ncloumn;
	char **resultp;

	sprintf(sql, "select * from usr where name = '%s' and pass = '%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;
	}
	else
	{
		printf("get_table ok!\n");
	}

	// 查询成功,数据库中拥有此用户
	if(nrow == 1)
	{
		strcpy(msg->data, "OK");//这里把OK放进data里,client.c中才会那么比较
		send(acceptfd, msg, sizeof(MSG), 0);
		return 1;
	}

	if(nrow == 0) // 密码或者用户名错误,或者写else就可以
	{
		strcpy(msg->data,"usr/passwd wrong.");
		send(acceptfd, msg, sizeof(MSG), 0);
	}

	return 0;
}

int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 1;
}

int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 0;
}

运行结果: 

 5.3.查询模块

 时间格式转换问题     localtime(&t)         头文件: time.h

 代码:

server.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include<sqlite3.h>
#include<signal.h>
#include<time.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);


void  do_register(int acceptfd, MSG *msg,sqlite3 *db);


int do_login(int acceptfd, MSG *msg,sqlite3 *db);

int do_query(int acceptfd, MSG *msg,sqlite3 *db);

int do_history(int acceptfd, MSG *msg,sqlite3 *db);


// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG msg;
	sqlite3 *db;
	int acceptfd;
	pid_t pid;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//打开数据库
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open DATABASE success\n");
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

	//绑定(建立套接字与地址联系)
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("fail to bind");
		return -1;
	}

	//将套接字设为监听模式
	if(listen(sockfd,5) < 0)//监听个数5
	{
		printf("fail to listen");
		return -1;
	}

	//处理僵尸进程
	signal(SIGCHLD,SIG_IGN);

	while(1)
	{
		if((acceptfd = accept(sockfd,NULL,NULL)) < 0)
		{
			perror("fail to accept");
			return -1;
		}
		//如果成功了,应创建父子进程,父进程永远接收客户端请求,子进程处理请求的具体内容
		if((pid = fork()) < 0)
		{
			perror("fail to fork");
			return -1;
		}
		else if(pid == 0)
		{
			//处理客户端具体的消息(子进程)
			close(sockfd);//监听套接字不用了,关闭
			do_client(acceptfd,db);

		}
		else
		{
			//接收客户端的请求(父进程)
			close(acceptfd);//接收套接字不用了,关闭
		}
	}


	return 0;
}


int do_client(int acceptfd,sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd,&msg,sizeof(msg),0) > 0)//接收成功
	{
		printf("type:%d\n", msg.type);//打印消息类型
		switch(msg.type)
		{
		case R:
			do_register(acceptfd, &msg, db);
			break;
		case L:
			do_login(acceptfd, &msg, db);
			break;
		case Q:
			do_query(acceptfd, &msg, db);
			break;
		case H:
			do_history(acceptfd, &msg, db);
			break;
		default:
			printf("Invalid data msg.\n");
		}

	}

	printf("client exit.\n");//客户端退出就会跳出while循环
	close(acceptfd);
	exit(0);

	return 0;
}


void  do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char * errmsg;
	char sql[512];

	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
	printf("%s\n", sql);

	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		strcpy(msg->data, "usr name already exist.");
	}
	else
	{
		printf("client  register ok!\n");
		strcpy(msg->data, "OK!");
	}

	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		return ;
	}

	return ;
}

int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sql[512] = {};//128太小会警告
	char *errmsg;
	int nrow;
	int ncloumn;
	char **resultp;

	sprintf(sql, "select * from usr where name = '%s' and pass = '%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;
	}
	else
	{
		printf("get_table ok!\n");
	}

	// 查询成功,数据库中拥有此用户
	if(nrow == 1)
	{
		strcpy(msg->data, "OK");//这里把OK放进data里,client.c中才会那么比较
		send(acceptfd, msg, sizeof(MSG), 0);
		return 1;
	}

	if(nrow == 0) // 密码或者用户名错误,或者写else就可以
	{
		strcpy(msg->data,"usr/passwd wrong.");
		send(acceptfd, msg, sizeof(MSG), 0);
	}

	return 0;
}



int do_searchword(int acceptfd, MSG *msg, char word[])
{
	FILE * fp;
	int len = 0;
	char temp[512] = {};
	int result;
	char *p;


	//打开文件,读取文件,进行比对

	if((fp = fopen("dict.txt", "r")) == NULL)
	{
		perror("fail to fopen.\n");
		strcpy(msg->data, "Failed to open dict.txt");
		send(acceptfd, msg, sizeof(MSG), 0);
		return -1;
	}

	//打印出,客户端要查询的单词
	len = strlen(word);
	printf("%s , len = %d\n", word, len);

	//读文件,来查询单词
	while(fgets(temp, 512, fp) != NULL)//只有一行,所以用fgets
	{

		//	printf("temp:%s\n", temp);

		// abandon  ab
		result = strncmp(temp,word,len);//word是要查询的单词,temp是单词表,单词表里单词是按照顺序排列的,所以会从小往大比

		if(result < 0)//还没找到,word在下面,继续找
		{
			continue;
		}
		if(result > 0 || ((result == 0) && (temp[len]!=' ')))//(已经找过了,但是上面没有,说明单词表里没有这个单词)(注意temp作为表头,是一个指针,后面两个条件放在一起,就是单词前面一样(abandon,aban明显abandon的d不是空格),说明没有这个单词)
		{
			break;
		}

		// 表示找到了,查询的单词
		p = temp + len; //  abandon   v.akdsf dafsjkj p指向相当于abandon后的第一个空格
						//	printf("found word:%s\n", p);
		while(*p == ' ')
		{
			p++;
		}

		// 找到了注释,跳跃过所有的空格
		strcpy(msg->data, p);
		printf("found word:%s\n", msg->data);

		// 注释拷贝完毕之后,应该关闭文件
		fclose(fp);
		return 1;
	}

	fclose(fp);

	return 0;
}

//获取时间
int get_date(char *date)
{
	time_t t;
	struct tm *tp;//定义时间结构体

	time(&t);//获取1970年到现在的秒数
	//printf("second=%d\n",(int)t);

	//进行时间格式转换
	tp = localtime(&t);

	sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min , tp->tm_sec);
	printf("get date:%s\n", date);

	return 0;
}



int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
	char word[64];
	int found = 0;
	char date[128] = {};
	char sql[512] = {};//128不够
	char *errmsg;

	//拿出msg结构体中,要查询的单词
	strcpy(word, msg->data);

	found = do_searchword(acceptfd, msg, word);
	printf("查询一个单词完毕.\n");

	// 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。
	if(found == 1)
	{
		// 需要获取系统时间 
		get_date(date);

		sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);

		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			printf("%s\n", errmsg);
			return -1;
		}
		else
		{
			printf("Insert record done.\n");
		}

	}
	else  //表示没有找到
	{
		strcpy(msg->data, "Not found!");
	}

	// 将查询的结果,发送给客户端
	send(acceptfd, msg, sizeof(MSG), 0);

	return 0;
}

int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
	printf("login ...\n");

	return 0;
}

client.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;


int  do_register(int sockfd, MSG *msg)
{
	msg->type = R;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)//(msg已经是指针,不用取地址)是大写MSG不是小写,小写是地址(永远4字节)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	// ok !  or  usr alread exist.
	printf("%s\n", msg->data);

	return 0;
}

int do_login(int sockfd, MSG *msg)
{
	msg->type = L;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	if(strncmp(msg->data, "OK", 3) == 0)//看看是否为OK,比较3位因为还有\0
	{
		printf("Login ok!\n");
		return 1;
	}
	else 
	{
		printf("%s\n", msg->data);
	}

	return 0;
}

int do_query(int sockfd, MSG *msg)
{
	msg->type = Q;
	puts("--------------");

	while(1)
	{
		printf("Input word:");
		scanf("%s", msg->data);
		getchar();

		//客户端,输入#号,返回到上一级菜单
		if(strncmp(msg->data, "#", 1) == 0)
			break;

		//将要查询的单词发送给服务器
		if(send(sockfd,msg, sizeof(MSG), 0) < 0)
		{
			printf("Fail to send.\n");
			return -1;
		}

		// 等待接受服务器,传递回来的单词的注释信息
		if(recv(sockfd, msg,sizeof(MSG), 0) < 0)
		{
			printf("Fail to recv.\n");
			return -1;
		}
		printf("%s\n", msg->data);
	}
		
	return 0;
}

int do_history(int sockfd, MSG *msg)
{
	printf("login ...\n");

	return 0;
}

	// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG  msg;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

		//连接服务器
		if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
		{
			perror("fail to connect");
			return -1;
		}

	//一级菜单
	while(1)
	{
		printf("***************************************************\n");
		printf("* 1.register          2.login           3.quit    *\n");
		printf("***************************************************\n");
		printf("please choose:");
		scanf("%d",&n);

		getchar();

		switch(n)
		{
		case 1:
			do_register(sockfd, &msg);
			break;
		case 2:
			if(do_login(sockfd, &msg) == 1)
			{
				goto next;
			}
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}

	}

	//二级菜单
next:
	while(1)
	{
		printf("*****************************************************\n");
		printf("* 1.query_word   2.history_record   3.quit          *\n");
		printf("*****************************************************\n");
		printf("Please choose:");
		scanf("%d", &n);
		getchar();

		switch(n)
		{
		case 1:
			do_query(sockfd, &msg);
			break;
		case 2:
			do_history(sockfd, &msg);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default :
			printf("Invalid data cmd.\n");
		}

	}

	return 0;
}

执行结果:

 

5.4 历史记录查询模块

 难点:为什么搞不懂回调函数呀aaaa

server.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include<sqlite3.h>
#include<signal.h>
#include<time.h>

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

#define DATABASE "my.db"

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;

int do_client(int acceptfd,sqlite3 *db);


void  do_register(int acceptfd, MSG *msg,sqlite3 *db);


int do_login(int acceptfd, MSG *msg,sqlite3 *db);

int do_query(int acceptfd, MSG *msg,sqlite3 *db);

int do_history(int acceptfd, MSG *msg,sqlite3 *db);
int history_callback(void* arg,int f_num,char** f_value,char** f_name);
int do_searchword(int acceptfd, MSG *msg, char word[]);
int get_date(char *date);

// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG msg;
	sqlite3 *db;
	int acceptfd;
	pid_t pid;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//打开数据库
	if(sqlite3_open(DATABASE,&db) != SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(db));
		return -1;
	}
	else
	{
		printf("open DATABASE success\n");
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

	//绑定(建立套接字与地址联系)
	if(bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
	{
		perror("fail to bind");
		return -1;
	}

	//将套接字设为监听模式
	if(listen(sockfd,5) < 0)//监听个数5
	{
		printf("fail to listen");
		return -1;
	}

	//处理僵尸进程
	signal(SIGCHLD,SIG_IGN);

	while(1)
	{
		if((acceptfd = accept(sockfd,NULL,NULL)) < 0)
		{
			perror("fail to accept");
			return -1;
		}
		//如果成功了,应创建父子进程,父进程永远接收客户端请求,子进程处理请求的具体内容
		if((pid = fork()) < 0)
		{
			perror("fail to fork");
			return -1;
		}
		else if(pid == 0)
		{
			//处理客户端具体的消息(子进程)
			close(sockfd);//监听套接字不用了,关闭
			do_client(acceptfd,db);

		}
		else
		{
			//接收客户端的请求(父进程)
			close(acceptfd);//接收套接字不用了,关闭
		}
	}


	return 0;
}


int do_client(int acceptfd,sqlite3 *db)
{
	MSG msg;
	while(recv(acceptfd,&msg,sizeof(msg),0) > 0)//接收成功
	{
		printf("type:%d\n", msg.type);//打印消息类型
		switch(msg.type)
		{
		case R:
			do_register(acceptfd, &msg, db);
			break;
		case L:
			do_login(acceptfd, &msg, db);
			break;
		case Q:
			do_query(acceptfd, &msg, db);
			break;
		case H:
			do_history(acceptfd, &msg, db);
			break;
		default:
			printf("Invalid data msg.\n");
		}

	}

	printf("client exit.\n");//客户端退出就会跳出while循环
	close(acceptfd);
	exit(0);

	return 0;
}


void  do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char * errmsg;
	char sql[512];

	sprintf(sql, "insert into usr values('%s', %s);", msg->name, msg->data);
	printf("%s\n", sql);

	if(sqlite3_exec(db,sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		printf("%s\n", errmsg);
		strcpy(msg->data, "usr name already exist.");
	}
	else
	{
		printf("client  register ok!\n");
		strcpy(msg->data, "OK!");
	}

	if(send(acceptfd, msg, sizeof(MSG), 0) < 0)
	{
		perror("fail to send");
		return ;
	}

	return ;
}

int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sql[512] = {};//128太小会警告
	char *errmsg;
	int nrow;
	int ncloumn;
	char **resultp;

	sprintf(sql, "select * from usr where name = '%s' and pass = '%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;
	}
	else
	{
		printf("get_table ok!\n");
	}

	// 查询成功,数据库中拥有此用户
	if(nrow == 1)
	{
		strcpy(msg->data, "OK");//这里把OK放进data里,client.c中才会那么比较
		send(acceptfd, msg, sizeof(MSG), 0);
		return 1;
	}

	if(nrow == 0) // 密码或者用户名错误,或者写else就可以
	{
		strcpy(msg->data,"usr/passwd wrong.");
		send(acceptfd, msg, sizeof(MSG), 0);
	}

	return 0;
}



int do_searchword(int acceptfd, MSG *msg, char word[])
{
	FILE * fp;
	int len = 0;
	char temp[512] = {};
	int result;
	char *p;


	//打开文件,读取文件,进行比对

	if((fp = fopen("dict.txt", "r")) == NULL)
	{
		perror("fail to fopen.\n");
		strcpy(msg->data, "Failed to open dict.txt");
		send(acceptfd, msg, sizeof(MSG), 0);
		return -1;
	}

	//打印出,客户端要查询的单词
	len = strlen(word);
	printf("%s , len = %d\n", word, len);

	//读文件,来查询单词
	while(fgets(temp, 512, fp) != NULL)//只有一行,所以用fgets
	{

		//	printf("temp:%s\n", temp);

		// abandon  ab
		result = strncmp(temp,word,len);//word是要查询的单词,temp是单词表,单词表里单词是按照顺序排列的,所以会从小往大比

		if(result < 0)//还没找到,word在下面,继续找
		{
			continue;
		}
		if(result > 0 || ((result == 0) && (temp[len]!=' ')))//(已经找过了,但是上面没有,说明单词表里没有这个单词)(注意temp作为表头,是一个指针,后面两个条件放在一起,就是单词前面一样(abandon,aban明显abandon的d不是空格),说明没有这个单词)
		{
			break;
		}

		// 表示找到了,查询的单词
		p = temp + len; //  abandon   v.akdsf dafsjkj p指向相当于abandon后的第一个空格
						//	printf("found word:%s\n", p);
		while(*p == ' ')
		{
			p++;
		}

		// 找到了注释,跳跃过所有的空格
		strcpy(msg->data, p);
		printf("found word:%s\n", msg->data);

		// 注释拷贝完毕之后,应该关闭文件
		fclose(fp);
		return 1;
	}

	fclose(fp);

	return 0;
}

//获取时间
int get_date(char *date)
{
	time_t t;
	struct tm *tp;//定义时间结构体

	time(&t);//获取1970年到现在的秒数
	//printf("second=%d\n",(int)t);

	//进行时间格式转换
	tp = localtime(&t);

	sprintf(date, "%d-%d-%d %d:%d:%d", tp->tm_year + 1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min , tp->tm_sec);
	printf("get date:%s\n", date);

	return 0;
}



int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
	char word[64];
	int found = 0;
	char date[128] = {};
	char sql[512] = {};//128不够
	char *errmsg;

	//拿出msg结构体中,要查询的单词
	strcpy(word, msg->data);

	found = do_searchword(acceptfd, msg, word);
	printf("查询一个单词完毕.\n");

	// 表示找到了单词,那么此时应该将 用户名,时间,单词,插入到历史记录表中去。
	if(found == 1)
	{
		// 需要获取系统时间 
		get_date(date);

		sprintf(sql, "insert into record values('%s', '%s', '%s')", msg->name, date, word);

		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
		{
			printf("%s\n", errmsg);
			return -1;
		}
		else
		{
			printf("Insert record done.\n");
		}

	}
	else  //表示没有找到
	{
		strcpy(msg->data, "Not found!");
	}

	// 将查询的结果,发送给客户端
	send(acceptfd, msg, sizeof(MSG), 0);

	return 0;
}


// 得到查询结果,并且需要将历史记录发送给客户端
int history_callback(void* arg,int f_num,char** f_value,char** f_name)
{
	// record : name  , date  , word 
	int acceptfd;
	MSG msg;

	acceptfd = *((int *)arg);

	sprintf(msg.data, "%s , %s", f_value[1], f_value[2]);

	send(acceptfd, &msg, sizeof(MSG), 0);

	return 0;
}





int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
	char sql[128] = {};
	char *errmsg;

	sprintf(sql, "select * from record where name = '%s'", msg->name);

	//查询数据库
	if(sqlite3_exec(db, sql, history_callback,(void *)&acceptfd, &errmsg)!= SQLITE_OK)
	{
		printf("%s\n", errmsg);
	}
	else
	{
		printf("Query record done.\n");
	}

	// 所有的记录查询发送完毕之后,给客户端发出一个结束信息
	msg->data[0] = '\0';//这边的发\0,客户端才接收

	send(acceptfd, msg, sizeof(MSG), 0);

	return 0;
}

client.c:

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close

#define N 32

#define R 1     //user - register
#define L 2     //user - login
#define Q 3     //user - query
#define H 4     //user - history

//定义通信双方的结构体
typedef struct {
	int type;
	char name[N];
	char data[256];
}MSG;


int  do_register(int sockfd, MSG *msg)
{
	msg->type = R;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)//(msg已经是指针,不用取地址)是大写MSG不是小写,小写是地址(永远4字节)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	// ok !  or  usr alread exist.
	printf("%s\n", msg->data);

	return 0;
}

int do_login(int sockfd, MSG *msg)
{
	msg->type = L;

	printf("Input name:");
	scanf("%s", msg->name);
	getchar();

	printf("Input passwd:");
	scanf("%s", msg->data);

	if(send(sockfd, msg, sizeof(MSG),0) < 0)
	{
		printf("fail to send.\n");
		return -1;
	}

	if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
	{
		printf("Fail to recv.\n");
		return -1;
	}

	if(strncmp(msg->data, "OK", 3) == 0)//看看是否为OK,比较3位因为还有\0
	{
		printf("Login ok!\n");
		return 1;
	}
	else 
	{
		printf("%s\n", msg->data);
	}

	return 0;
}

int do_query(int sockfd, MSG *msg)
{
	msg->type = Q;
	puts("--------------");

	while(1)
	{
		printf("Input word:");
		scanf("%s", msg->data);
		getchar();

		//客户端,输入#号,返回到上一级菜单
		if(strncmp(msg->data, "#", 1) == 0)
			break;

		//将要查询的单词发送给服务器
		if(send(sockfd,msg, sizeof(MSG), 0) < 0)
		{
			printf("Fail to send.\n");
			return -1;
		}

		// 等待接受服务器,传递回来的单词的注释信息
		if(recv(sockfd, msg,sizeof(MSG), 0) < 0)
		{
			printf("Fail to recv.\n");
			return -1;
		}
		printf("%s\n", msg->data);
	}
		
	return 0;
}

int do_history(int sockfd, MSG *msg)
{
	
	msg->type = H;

	send(sockfd, msg, sizeof(MSG), 0);
	
	// 接受服务器,传递回来的历史记录信息
	while(1)
	{
		recv(sockfd, msg, sizeof(MSG), 0);

		if(msg->data[0] == '\0')
			break;

		//输出历史记录信息
		printf("%s\n", msg->data);
	}


	return 0;
}

	// ./server 192.168.***.***  10000(端口号) 分别是argv[0],argv[1],argv[2]
int main(int argc, const char *argv[])
{

	int sockfd;
	struct sockaddr_in serveraddr;//网络信息结构体
	int n;
	MSG  msg;

	if(argc != 3)
	{
		printf("Usage:%s serverip port.\n",argv[0]);//没有传IP和端口号,argv[0]表示从main函数将相应的参数接下来
		return -1;
	}

	//创建套接字
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)//等于-1说明失败
	{
		perror("fail to socket.\n");
		return -1;
	}

	bzero(&serveraddr, sizeof(serveraddr));//清空结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//将ip地址转换为二进制
	serveraddr.sin_port = htons(atoi(argv[2]));//将本机数据转换为网络数据,atoi功能将字符串转换为整数

		//连接服务器
		if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0)
		{
			perror("fail to connect");
			return -1;
		}

	//一级菜单
	while(1)
	{
		printf("***************************************************\n");
		printf("* 1.register          2.login           3.quit    *\n");
		printf("***************************************************\n");
		printf("please choose:");
		scanf("%d",&n);

		getchar();

		switch(n)
		{
		case 1:
			do_register(sockfd, &msg);
			break;
		case 2:
			if(do_login(sockfd, &msg) == 1)
			{
				goto next;
			}
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default:
			printf("Invalid data cmd.\n");
		}

	}

	//二级菜单
next:
	while(1)
	{
		printf("*****************************************************\n");
		printf("* 1.query_word   2.history_record   3.quit          *\n");
		printf("*****************************************************\n");
		printf("Please choose:");
		scanf("%d", &n);
		getchar();

		switch(n)
		{
		case 1:
			do_query(sockfd, &msg);
			break;
		case 2:
			do_history(sockfd, &msg);
			break;
		case 3:
			close(sockfd);
			exit(0);
			break;
		default :
			printf("Invalid data cmd.\n");
		}

	}

	return 0;
}

运行结果:

服务器端:

 用户1:

用户2:

 作业

day1-3: 第一题:提交在线词典项目功能实现相关代码(将源代码打包后上传)
1、在搭建的框架中实现并发,实现多客户端的注册登录
2、同时支持管理员(用户名:root,密码:1)和普通用户,提示:可以在通信的结构体中添加flag标志位判断是root用户还是普通用户
3、管理员可以查询所有用户的使用记录(将源代码打包后上传)
(作业要求:做作业的时候不要再翻看视频上的教程,对函数理解不明白的全部通过man手册去查看,
自己思考框架,使用makefile编译,然后将测试的记录和结果添加到readme.txt文件中提交上来,代码实现完成测试通过后
再提交作业,网络部分学习不写代码不测试看不出问题的,良好的习惯帮助你们快速成长。)

2. 第二题:通过分析现象来推导代码的实现===>ftp服务器的实现(提供参考框架,但是不提供参考代码):

fengjunhui@ubuntu:~/homework/ftpserver$ ls
client.c Makefile server.c

fengjunhui@ubuntu:~/homework/ftpserver$ make
gcc -g -c server.c -o server.o
gcc server.o -o server
gcc -g -c client.c -o client.o
gcc client.o -o client

fengjunhui@ubuntu:~/homework/ftpserver$ ls
client client.c client.o Makefile server server.c server.o
(*.o文件可以删掉)

fengjunhui@ubuntu:~/homework/ftpserver$ mkdir feng
fengjunhui@ubuntu:~/homework/ftpserver$ mv client feng/

fengjunhui@ubuntu:~/homework/ftpserver/feng$ ls
client


服务器端等待链接请求:
fengjunhui@ubuntu:~/homework/ftpserver$ ./server 192.168.1.200 9999 (指定你本机Ubuntu的ip和>5000的端口号)
server sockfd :3

注意:编译生成可执行文件之后./server 和 ./client的执行在不同的目录
客户端登录
fengjunhui@ubuntu:~/homework/ftpserver/feng$ ./client 192.168.1.200 9999 (指定你本机Ubuntu的ip和>5000的端口号)
**************************
***请输入 help 查看选项***
**************************
input your choice: >>>

input your choice: >>> help
*****************************************************
********输入/功能*************************************
********list :查看服务器所在目录的所有文件************
********get filename 下载服务器目录的文件************
********put filename: 上传文件到服务器****************
********quit :关闭客户端 *****************************
******************************************************

功能1----------------查看服务器端的文件列表信息
input your choice: >>> list
***Makefile
***server
***server.c
.......
服务器目录已经接收完毕

服务器应答
目录清单已经成功发送


功能2 --------------从服务器段下载文件到客户端
input your choice: >>> get server.c
下载完毕
ls 客户端所在目录可以看到server.c的文件

服务器提示:
文件传送完成


功能3 ------------向服务器端上传文件
input your choice: >>> put hello.c(自己定义一个文件,输出hello world就行)
上传完毕

服务器提示:
接收文件成功
client client.c client.o Makefile server server.c server.o hello.c


功能4--------------客户端退出,服务器继续等待链接
input your choice: >>> quit

服务器端打印客户端退出

要求:
上传或下载的某个功能要采用多进程或多线程的方式实现
每个客户端访问的日期信息要显示在服务器上
要求独立实现,不要问老师,实在不会的也不要问老师,自己尽可能的逼着自己独立去实现,
独立去调试,可以参考level7-8部分在线词典的项目,但是一定要自己吃透。

项目提交内容:
1、要求使用makefile编译
2、将你测试执行的结果写到readme.txt文档当中,如果实在调不通的话,将你当前阶段遇到的问题写到readme.txt当中,
3、中间阶段不会的先通过man来查找函数,不要上来就是翻笔记和视频
4、要求自己独立设计框架,框架作为单独的内容提交上来
5、要求自己制定通信协议,将协议的每一个具体细节和作用注释出来
6、要求尽可能把把level4-5-6-7和level1-2-3尽可能的结合起来去完成整个的作业
7、要求提交可调试的代码上来,不要留言说我不会
7、预祝大家成功的搞定这个小项目,预祝大家有质的提升
注意:
这道题不提供参考答案!!!

第一题:

client.c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>      
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include<fcntl.h>
#include<unistd.h>

#define N 32

#define R 1  //usr--register
#define L 2  //usr--login
#define Q 3  //usr--query
#define H 4  //root--history
#define Y 5  //root--login
#define X 6  //root--user record

//定义通信双方的MSG信息结构体
typedef struct {
    int type;
    char name[N];
    char data[256];
}MSG;

int do_regiser(int sockfd, MSG *msg);
int do_login(int sockfd, MSG *msg);
int do_login_root(int sockfd, MSG *msg);
int do_query(int sockfd, MSG *msg);
int do_history(int sockfd, MSG *msg);
int do_user_record(int sockfd, MSG *msg);


int main(int argc, const char *argv[])
{
    int sockfd;
    struct sockaddr_in serveraddr;
    int num;
    MSG msg;

    if(argc != 3)
    {
        printf("Usage :%s serverip port.\n", argv[0]);
        return -1;
    }

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("fail to socket!\n");
        return -1;
    }

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);    //点分形式转为二进制数据
    serveraddr.sin_port = htons(atoi(argv[2]));  //先将字符型转为int型数据,再HBD->NBD 

    if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("fail to connect\n");
        return -1;
    }

    printf("Client starting...\n");

    while(1)
    {
        printf("***************************************************\n");
        printf("*     1.regiser  2.login  3.root login  4.quit    *\n");
        printf("***************************************************\n");
        printf("Please choose:");

        scanf("%d",&num);
        getchar();  //每次输入完毕会产生垃圾字符,清除

        switch(num)
        {
            case 1:
                do_regiser(sockfd, &msg);
                break;
            case 2:
                if(do_login(sockfd,&msg) == 1)
                {
                    goto next; //登录成功,跳到二级菜单
                }
                break;
            case 3:
                if(do_login_root(sockfd, &msg) == 1)
                {
                    goto next_root;
                }
            case 4:
                close(sockfd);
                exit(0);
                break;
            default:
                printf("Invalid data cmd.\n");
        }
    }

next:   //二级菜单
    while(1)
    {
        printf("***************************************************\n");
        printf("*        1.query word     2.quit        \n");
        printf("***************************************************\n");
        printf("Please choose:");

        scanf("%d",&num);
        getchar();  //每次输入完毕会产生垃圾字符,清除

        switch(num)
        {
            case 1:
                do_query(sockfd, &msg);
                break;
            case 2:
                close(sockfd);
                exit(1);
                break;
            default:
                printf("Invalid data cmd.\n");
        }

    }


next_root:   //管理员二级菜单
    while(1)
    {
        printf("***************************************************\n");
        printf("*    1.query word   2.history  3.user record  4.quit    *\n");
        printf("***************************************************\n");
        printf("Please choose:");

        scanf("%d",&num);
        getchar();  //每次输入完毕会产生垃圾字符,清除

        switch(num)
        {
            case 1:
                do_query(sockfd, &msg);
                break;
            case 2:
                do_history(sockfd, &msg);
                break;
            case 3:
                do_user_record(sockfd, &msg);
                break;
            case 4:
                close(sockfd);
                exit(1);
                break;
            default:
                printf("Invalid data cmd.\n");
        }

    }
    return 0;
}

int do_regiser(int sockfd, MSG *msg)
{
    msg->type = R;
    printf("Intput name:");
    scanf("%s",msg->name);
    getchar();

    printf("Intput password:");
    scanf("%s",msg->data);

    if(send(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to send.\n");
        return -1;
    }

    if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to recv.\n");
        exit(1);
    }
    
    //ok! or usr already exist
    printf("%s\n",msg->data);

    return 0;

}

int do_login(int sockfd, MSG *msg)
{
    msg->type = L;

    printf("Intput name:");
    scanf("%s",msg->name);
    getchar();

    printf("Intput password:");
    scanf("%s",msg->data);

    if(send(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to send.\n");
        exit(1);
    }

    if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to recv.\n");
        return -1;
    }

    if(strncmp(msg->data, "ok", 3) == 0)
    {
        printf("Login ok!\n");
        return 1;
    }
    else
    {
        printf("%s\n",msg->data);
    }


    return 0;
}


int do_login_root(int sockfd, MSG *msg)
{
    msg->type = Y;

    printf("name: root");
    sprintf(msg->name,"root");
    getchar();

    printf("Intput password:");
    scanf("%s",msg->data);

    if(send(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to send.\n");
        exit(1);
    }

    if(recv(sockfd, msg, sizeof(MSG), 0) < 0)
    {
        printf("fail to recv.\n");
        return -1;
    }

    if(strncmp(msg->data, "ok", 3) == 0)
    {
        printf("ROOT Login ok!\n");
        return 1;
    }
    else
    {
        printf("%s\n",msg->data);
    }


    return 0;
}

int do_query(int sockfd, MSG *msg)
{
    msg->type = Q;
    puts("--------------------------\n");

    while(1)
    {
        printf("Intput word:");
        scanf("%s",msg->data);

        //客户端输入结束并返回上一层
        if(strncmp(msg->data,"#",1) == 0) //比较前1个字符,相同返0
            break;
    
        //将要查询的单词发送给服务器
        if(send(sockfd, msg, sizeof(MSG), 0) < 0)
        {
            printf("file to send.\n");
            return -1;
        }
        
        //等待接收服务器传递回来的注释单词信息
        if(recv(sockfd, msg, sizeof(MSG),0) < 0)
        {
            printf("file to recv.\n");
            return -1;
        }
        printf("%s\n",msg->data);
    }

    return 0;
}

int do_history(int sockfd, MSG *msg)
{
    msg->type = H;
    
    printf("Intput the user name:\n");
    printf("** name * query date * word **\n");
    scanf("%s",msg->name);
    send(sockfd, msg, sizeof(MSG), 0);

    //接收服务器传递回来的历史记录
    while(1)
    {
        recv(sockfd, msg, sizeof(MSG), 0);

        if(msg->data[0] == '\0')
            break;
        
        //输出历史记录信息
        printf("%s\n", msg->data);
    }
    return 0;
}

//查询用户的登录与密码记录
int do_user_record(int sockfd, MSG *msg)
{
    msg->type = X;
    printf("Intput the user name:\n");
    printf("** name * password * register date **\n");
    scanf("%s",msg->name);
    send(sockfd, msg, sizeof(MSG), 0);

    //接收服务器传递回来的历史记录
    while(1)
    {
        recv(sockfd, msg, sizeof(MSG), 0);

        if(msg->data[0] == '\0')
            break;
        
        //输出历史记录信息
        printf("%s\n", msg->data);
    }
    return 0;

}

server.c:

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



#define N 32
#define DATABASE "dic.db"

#define R 1  //usr--register
#define L 2  //usr--login
#define Q 3  //usr--query
#define H 4  //root--history
#define Y 5  //root--login
#define X 6  //root--user record

//定义通信双方的MSG信息结构体
typedef struct {
    int type;
    char name[N];
    char data[256];
}MSG;

int do_client(int acceptfd, sqlite3 *db);
void do_regiser(int acceptfd, MSG *msg, sqlite3 *db);
int do_login(int acceptfd, MSG *msg, sqlite3 *db);
int do_login_root(int acceptfd, MSG *msg, sqlite3 *db);
int do_query(int acceptfd, MSG *msg, sqlite3 *db);
int do_history(int acceptfd, MSG *msg, sqlite3 *db);
int history_callback(void *arg, int f_num,char** f_value ,char** f_name);
int do_searchword(int acceptfd,MSG *msg, char word[]);
int get_date(char *date);
int do_user_record(int acceptfd, MSG *msg, sqlite3 *db);
int user_record_callback(void *arg, int f_num,char** f_value ,char** f_name);


// ./server 192.168.31.123 5001
int main(int argc, const char *argv[])
{
    int sockfd;
    int acceptfd;
    struct sockaddr_in serveraddr;
    int num;
    MSG msg; 
    sqlite3 *db;
    pid_t pid;
    char *errmsg;
    char sql[128];

    if(argc != 3)
    {
        printf("Usage :%s server ip port.\n", argv[0]);
        return -1;
    }

    /*打开数据库 */
    if(sqlite3_open(DATABASE, &db) != SQLITE_OK)
    {
        printf("%s\n", sqlite3_errmsg(db));
        return -1;
    }
    else
    {
        printf("open DATABASE success.\n");
    }
    
    //在dic,db 里面创建表格用户密码 user 与单词记录表 record      
    //创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("fail to socket!\n");
        return -1;
    }

    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);    //点分形式转为二进制数据
    serveraddr.sin_port = htons(atoi(argv[2]));  //先将字符型转为int型数据,再HBD->NBD 

    /*地址快速重用 
    int b_reuse = -1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
    */

    /*绑定 */
    if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("file to bind.\n");
        return -1;
    }
    
    /*将套接字设置为监听模式*/
    if(listen(sockfd, 5) < 0)
    {
        perror("fail to listen.\n");
        return -1;
    }

    //处理僵尸进程
    signal(SIGCHLD, SIG_IGN);
    //SIG_IGN,信号处理方式设置为忽略,内核就会将僵尸子进程交给init处理

    printf("Server starting ... OK!\n");

    while(1)
    {
        if((acceptfd = accept(sockfd, NULL, NULL)) < 0)
        {
            perror("fail to accept.\n");
            return -1;
        }
        
        printf("Connect success!\n");

        if((pid = fork()) < 0)
        {
            perror("fail to fork.");
            return -1;
        }
        else if(pid == 0) //子进程
        {
            //处理客户端的具体消息
            close(sockfd);  //关闭不需要的文件描述符,节省资源
            do_client(acceptfd, db);
            return 0;
        }
        else  //父进程,用来接收客户端的请求
        {
             close(acceptfd);
        }
    }
    close(acceptfd);
    return 0;
}

int do_client(int acceptfd, sqlite3 *db)
{
    MSG msg;
    //客户端关闭也会关闭套接字,进而跳出while循环
    while(recv(acceptfd, &msg, sizeof(msg), 0) > 0) //接收成功
    {
        printf("type:%d\n", msg.type);
        switch(msg.type)
        {
            case R:
                do_regiser(acceptfd, &msg, db);
                break;
            case L:
                do_login(acceptfd, &msg, db);
                break;
            case Y:
                do_login_root(acceptfd, &msg, db);
                break;
            case Q:
                do_query(acceptfd, &msg, db);
                break;
            case H:
                do_history(acceptfd, &msg, db);
                break;
            case X:
                do_user_record(acceptfd, &msg, db);
            default:
                printf("Invalid data msg.\n");
        }
    }

    printf("client exit.\n");
    close(acceptfd);
    exit(0);

    return 0;
}

void do_regiser(int acceptfd, MSG *msg, sqlite3 *db)
{
    char *errmsg;
    char sql[512];
    char date[128];
    //获取系统时间
    get_date(date);

    sprintf(sql,"insert into user values('%s','%s' ,'%s');", msg->name, msg->data, date);
    printf("%s\n",sql);

    if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
    {
        printf("%s\n",errmsg);
        strcpy(msg->data, "user name already exit.");
    }
    else
    {
        printf("client register ok!\n");
        strcpy(msg->data, "ok"); //将OK拷到data中,发给client
    }
    
    if(send(acceptfd, msg, sizeof(MSG), 0)<0)
    {
        perror("fail to send");
        return;
    }

    return;
}

int do_login(int acceptfd, MSG *msg, sqlite3 *db)
{
    char sql[512];
    char *errmsg;
    int nrow;
    int ncloumn;
    char **resultp;

    sprintf(sql,"select * from user 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;
    }
    else
    {
        printf("get_table ok!\n");
    }

    //查询成功,数据库拥有此用户
    if(nrow == 1)
    {
        strcpy(msg->data, "ok");
        send(acceptfd, msg, sizeof(MSG),0);
        return 1;
    }
    else  //密码或用户名错误
    {
        strcpy(msg->data,"usr/passwd wrong.");
        send(acceptfd, msg, sizeof(MSG),0);
    }
    return 0;
}

int do_login_root(int acceptfd, MSG *msg, sqlite3 *db)
{
    char sql[512];
    char *errmsg;
    int nrow;
    int ncloumn;
    char **resultp;

    sprintf(sql,"select * from user where name = 'root' and passwd = '%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;
    }
    else
    {
        printf("get_table ok!\n");
    }

    //查询成功,数据库拥有此用户
    if(nrow == 1)
    {
        strcpy(msg->data, "ok");
        send(acceptfd, msg, sizeof(MSG),0);
        return 1;
    }
    else  //密码或用户名错误
    {
        strcpy(msg->data,"passwd wrong.");
        send(acceptfd, msg, sizeof(MSG),0);
    }

    return 0;
}

int do_query(int acceptfd, MSG *msg, sqlite3 *db)
{
    char word[64];
    int found = 0;
    char date[128];
    char sql[512];
    char *errmsg;

    //拿出msg结构体中,要查询的单词
    strcpy(word, msg->data);

    found = do_searchword(acceptfd,msg, word);

    //表找到了单词,此时应将 name/date/word 插入history.text
    if(found == 1) 
    {
        //获取系统时间
        get_date(date);

        //添加历史记录
        sprintf(sql,"insert into record values('%s','%s','%s')", msg->name, date, word);
    

        if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
        {
            printf("%s\n",errmsg);
            return -1;
        }
        else
        {
            printf("Insert record done.\n");
        }
    }
    else //未找到
    {
        strcpy(msg->data,"Not found!");    
    }

    //将查询的结果,发送给客户端
    send(acceptfd, msg, sizeof(MSG), 0);

    return 0;
}

int do_history(int acceptfd, MSG *msg, sqlite3 *db)
{
    char sql[128];
    char *errmsg;

    
    sprintf(sql, "select * from record where name = '%s';",msg->name);

    //查询数据库
    if(sqlite3_exec(db, sql, history_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK)
    {
        printf("%s\n",errmsg);
    }
    else
    {
        printf("Query record done.\n");
    }

    //所有记录查询发送完毕,给客户端发送结束信息
    msg->data[0] = '\0';

    send(acceptfd, msg, sizeof(MSG),0);

    return 0;
}

int history_callback(void *arg, int f_num,char** f_value ,char** f_name)
{
    //record , name , date , word
    int acceptfd;
    MSG msg; //声明结构体变量
    acceptfd = *((int *)arg);

    sprintf(msg.data,"%s, %s , %s",f_value[0], f_value[1], f_value[2]); //不是msg->data

    send(acceptfd, &msg, sizeof(MSG), 0);

    return 0;
}

int do_searchword(int acceptfd,MSG *msg, char word[])
{
    FILE * fp;
    int len = 0;
    char temp[512] = {};
    int result;
    char *p;
    
    //打开文件,读取文件,进行对比
    if((fp = fopen("dict.txt", "r")) == NULL)
    {
        perror("fail to fopen.\n");
        strcpy(msg->data,"Failed to open dict.txt");
        send(acceptfd, msg, sizeof(MSG), 0);
        return -1;
    }

    //打印出,客户端要查询的单词
    len = strlen(word);
    printf("%s, len = %d\n", word, len);

    //读文件,来查询单词
    while(fgets(temp,512,fp) != NULL)
    {
    
    //    printf("temp:%s\n", temp); //打印从dict.txt获取的字符
        result = strncmp(temp ,word, len);

        if(result < 0) //eg:查询Apple,app<apple,appble<pple
        {
            continue;
        }
        if(result > 0 || ((result == 0) && (temp[len] != ' ')))
        {
            break;
        }

        //表示找到查询的单词
        p = temp + len; //指针移动到单词的末尾
        while(*p == ' ')
        {
            p++;
        }

        //找到了注释,跳跃过所有的空格
        strcpy(msg->data, p);
        printf("found word:%s\n",msg->data);

        //注释拷贝完毕,关闭文件
        fclose(fp);
        
        return 1;
    }

    fclose(fp);

    return 0;
}

int get_date(char *date)
{
    time_t t;
    struct tm *tp;

    time(&t);

    //进行时间格式转换
    tp = localtime(&t);

    sprintf(date, "%d-%d-%d %d:%d:%d",tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, 
            tp->tm_hour, tp->tm_min, tp->tm_sec);


    return 0;
}


//查询用户的注册时间与账户及密码
int do_user_record(int acceptfd, MSG *msg, sqlite3 *db)
{
    char sql[128];
    char *errmsg;

    
    sprintf(sql, "select * from user where name = '%s';",msg->name);

    //查询数据库
    if(sqlite3_exec(db, sql, user_record_callback, (void *)&acceptfd, &errmsg) != SQLITE_OK)
    {
        printf("%s\n",errmsg);
    }
    else
    {
        printf("Query user record done.\n");
    }

    //所有记录查询发送完毕,给客户端发送结束信息
    msg->data[0] = '\0';

    send(acceptfd, msg, sizeof(MSG),0);

    return 0;
}

int user_record_callback(void *arg, int f_num,char** f_value ,char** f_name)
{
    //record , name , passwd , date
    int acceptfd;
    MSG msg; //声明结构体变量
    acceptfd = *((int *)arg);

    sprintf(msg.data,"%s, %s , %s",f_value[0], f_value[1], f_value[2]); //不是msg->data

    send(acceptfd, &msg, sizeof(MSG), 0);

    return 0;
}

运行问题:无法连接,可能端口号不能用

 

第二题: 我不会

见链接ftp服务器实现_午饭要阳光的博客-CSDN博客

  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值