嵌入式Linux开发学习记录(6)

一,数据库sqlite3学习

数据库命令:
      1)系统命令 , 都以'.'开头
         .exit 
         .quit
         .table   查看表
         .schema  查看表的结构
          
      2)sql语句, 都以‘;’结尾

        1-- 创建一张表
            create table stuinfo(id integer, name text, age integer, score float);
        
        2-- 插入一条记录
            insert into stuinfo values(1001, 'zhangsan', 18, 80);
            insert into stuinfo (id, name, score) values(1002, 'lisi', 90);

        3-- 查看数据库记录
            select * from stuinfo;
            select * from stuinfo where score = 80;
            select * from stuinfo where score = 80 and name= 'zhangsan';
            select * from stuinfo where score = 80 or name='wangwu';
            select name,score from stuinfo;  查询指定的字段
            select * from stuinfo where score >= 85 and score < 90;

        4-- 删除一条记录
            delete from stuinfo where id=1003 and name='zhangsan';

        5-- 更新一条记录
            update stuinfo set age=20 where id=1003;
            update stuinfo set age=30, score = 82 where id=1003;

        6-- 删除一张表
            drop table stuinfo;

        7-- 增加一列
            alter table stuinfo add column sex char;

        8-- 删除一列
            create table stu as select id, name, score from stuinfo;
            drop table stuinfo;
            alter table stu rename to stuinfo;
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>

#define  DATABASE  "student.db"  // 定义数据库名称
#define  N  128  // 定义字符数组大小

// 插入记录函数
int do_insert(sqlite3 *db) {
	int id;
	char name[32] = {};
	char sex;
	int score;
	char sql[N] = {};
	char *errmsg;

	printf("Input id:");
	scanf("%d", &id);  // 输入学生ID

	printf("Input name:");
	scanf("%s", name);  // 输入学生姓名
	getchar();  // 读取换行符

	printf("Input sex:");
	scanf("%c", &sex);  // 输入学生性别

	printf("Input score:");
	scanf("%d", &score);  // 输入学生成绩

	sprintf(sql, "insert into stu values(%d, '%s', '%c', %d)", id, name, sex, score);  // 构建SQL插入语句

	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {  // 执行SQL语句
		printf("%s\n", errmsg);  // 输出错误信息
	} else {
		printf("Insert done.\n");  // 插入成功提示
	}

	return 0;
}

// 删除记录函数
int do_delete(sqlite3 *db) {
	int id;
	char sql[N] = {};
	char *errmsg;

	printf("Input id:");
	scanf("%d", &id);  // 输入要删除的学生ID

	sprintf(sql, "delete from stu where id = %d", id);  // 构建SQL删除语句

	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {  // 执行SQL语句
		printf("%s\n", errmsg);  // 输出错误信息
	} else {
		printf("Delete done.\n");  // 删除成功提示
	}

	return 0;
}

// 更新记录函数
int do_update(sqlite3 *db) {
	int id;
	char sql[N] = {};
	char name[32] = "zhangsan";
	char *errmsg;

	printf("Input id:");
	scanf("%d", &id);  // 输入要更新的学生ID

	sprintf(sql, "update stu set name='%s' where id=%d", name, id);  // 构建SQL更新语句

	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {  // 执行SQL语句
		printf("%s\n", errmsg);  // 输出错误信息
	} else {
		printf("update done.\n");  // 更新成功提示
	}

	return 0;
}

// 查询回调函数
int callback(void *arg, int f_num, char ** f_value, char ** f_name) {
	int i = 0;

	for(i = 0; i < f_num; i++) {
		printf("%-8s", f_value[i]);  // 输出查询结果
	}

	printf("++++++++++++++++++++++");
	putchar(10);

	return 0;
}

// 查询记录函数
int do_query(sqlite3 *db) {
	char *errmsg;
	char sql[N] = "select count(*) from stu where name='zhangsan';";  // 查询name为zhangsan的记录数量

	if(sqlite3_exec(db, sql, callback, NULL, &errmsg) != SQLITE_OK) {  // 执行SQL语句
		printf("%s", errmsg);  // 输出错误信息
	} else {
		printf("select done.\n");  // 查询成功提示
	}
}

// 另一种查询记录的函数
int do_query1(sqlite3 *db) {
	char *errmsg;
	char ** resultp;
	int nrow;
	int ncolumn;

	if(sqlite3_get_table(db, "select * from stu", &resultp, &nrow, &ncolumn, &errmsg) != SQLITE_OK) {  // 获取查询结果表
		printf("%s\n", errmsg);  // 输出错误信息
		return -1;
	} else {
		printf("query done.\n");  // 查询成功提示
	}

	int i = 0;
	int j = 0;
	int index = ncolumn;

	for(j = 0; j < ncolumn; j++) {
		printf("%-10s ", resultp[j]);  // 输出列名
	}
	putchar(10);

	for(i = 0; i < nrow; i++) {
		for(j = 0; j < ncolumn; j++) {
			printf("%-10s ", resultp[index++]);  // 输出查询结果
		}
		putchar(10);
	}

	return 0;
}

// 主函数
int main(int argc, const char *argv[]) {
	sqlite3 *db;
	char *errmsg;
	int n;

	if(sqlite3_open(DATABASE, &db) != SQLITE_OK) {  // 打开数据库
		printf("%s\n", sqlite3_errmsg(db));  // 输出错误信息
		return -1;
	} else {
		printf("open DATABASE success.\n");  // 打开数据库成功提示
	}

	if(sqlite3_exec(db, "create table if not exists stu(id int, name char , sex char , score int);",
				NULL, NULL, &errmsg) != SQLITE_OK) {  // 创建表
		printf("%s\n", errmsg);  // 输出错误信息
	} else {
		printf("Create or open table success.\n");  // 创建表成功提示
	}

	while(1) {
		printf("********************************************\n");
		printf("1: insert  2:query  3:delete 4:update 5:quit\n");
		printf("********************************************\n");
		printf("Please select:");
		scanf("%d", &n);  // 输入选择项

		switch(n) {
			case 1:
				do_insert(db);  // 插入记录
				break;
			case 2:
				do_query(db);  // 查询记录
				// do_query1(db);  // 另一种查询方式
				break;
			case 3:
				do_delete(db);  // 删除记录
				break;
			case 4:
				do_update(db);  // 更新记录
				break;
			case 5:
				printf("main exit.\n");
				sqlite3_close(db);  // 关闭数据库
				exit(0);  // 退出程序
				break;
			default :
				printf("Invalid data n.\n");  // 无效输入提示
		}
	}

	return 0;
}

运行效果

二,在线词典项目

实现一个基于TCP协议的字典查询系统,具备用户注册、登录、单词查询和历史记录查询的功能。

 服务器端逻辑:

1. 启动和初始化:

服务器启动时,创建一个TCP套接字,用于监听客户端的连接请求。
服务器绑定到指定的IP地址和端口号,以便客户端可以连接。
服务器进入监听模式,准备接受多个客户端的连接请求。

2. 处理连接:
服务器通过调用`accept`函数接受客户端的连接请求。
每当有新的客户端连接时,服务器创建一个子进程(通过`fork`),由子进程负责处理该客户端的所有请求,父进程继续监听新的连接。
通过设置信号处理函数`SIGCHLD`,父进程可以避免僵尸进程的产生。

3. 数据库操作:
服务器使用SQLite数据库存储用户信息和查询历史记录。
在服务器启动时,打开指定的SQLite数据库文件。
根据客户端的请求类型(注册、登录、查询单词、查询历史记录),服务器执行相应的SQL语句对数据库进行操作。

4. 请求处理:
注册(R):服务器从客户端接收注册请求,插入新的用户记录到数据库中。如果用户名已存在,返回错误信息,否则返回注册成功信息。
登录(L):服务器接收登录请求,查询数据库验证用户名和密码。如果验证成功,返回登录成功信息,否则返回错误信息。
查询单词(Q):服务器接收单词查询请求,从本地字典文件中查找单词的定义。如果找到单词,将定义返回给客户端,并记录查询历史;如果未找到,返回未找到的信息。
 查询历史记录(H):服务器接收查询历史记录请求,从数据库中查询该用户的所有查询记录,并逐条返回给客户端。

客户端逻辑:

1. 启动和连接:
客户端启动时,创建一个TCP套接字,并连接到服务器的指定IP地址和端口号。

2. 用户交互:
客户端通过命令行界面与用户交互,提供注册、登录、查询单词和查询历史记录的功能菜单。
用户选择相应的功能后,客户端根据用户输入构建消息结构体,并发送到服务器。

3. 请求发送和接收:
注册:用户选择注册功能后,输入用户名和密码,客户端将这些信息发送到服务器,并接收服务器的响应信息(成功或失败)。
登录:用户选择登录功能后,输入用户名和密码,客户端将这些信息发送到服务器,并接收服务器的响应信息。如果登录成功,客户端进入下一级菜单。
查询单词:用户选择查询单词功能后,输入要查询的单词,客户端将单词发送到服务器,并接收服务器返回的单词定义。
查询历史记录:用户选择查询历史记录功能后,客户端发送请求到服务器,并接收服务器返回的历史记录信息。

具体的交互过程:

1. 启动服务器:
服务器启动,绑定到指定的IP和端口,并进入监听模式。

2. 客户端连接:
客户端启动后,连接到服务器。
服务器接受客户端的连接请求,创建子进程处理该客户端。

3. 用户操作:
用户在客户端选择注册、登录、查询单词或查询历史记录。
客户端根据用户选择构建消息,发送到服务器。

4. 服务器处理:
服务器子进程接收客户端的请求,根据请求类型进行相应的数据库操作或文件操作,并构建响应消息发送回客户端。

5. 客户端接收响应:
客户端接收服务器的响应信息,并在命令行界面显示结果。

6. 循环处理:
用户可以多次执行不同的操作,客户端和服务器保持通信,直到用户选择退出。

 客户端代码

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

#define   N  32                 // 定义用户名的最大长度

#define  R  1   // 定义操作类型为注册
#define  L  2   // 定义操作类型为登录
#define  Q  3   // 定义操作类型为查询
#define  H  4   // 定义操作类型为历史记录

// 定义通信双方的信息结构体
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);  // 获取用户名并存储到msg结构体中
	getchar();  // 读取并丢弃多余的换行符

	printf("Input passwd:");  // 提示输入密码
	scanf("%s", msg->data);  // 获取密码并存储到msg结构体中

	if(send(sockfd, msg, sizeof(MSG),0) < 0)  // 将msg发送给服务器
	{
		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_login(int sockfd, MSG *msg)
{
	msg->type = L;  // 设置操作类型为登录

	printf("Input name:");  // 提示输入用户名
	scanf("%s", msg->name);  // 获取用户名并存储到msg结构体中
	getchar();  // 读取并丢弃多余的换行符

	printf("Input passwd:");  // 提示输入密码
	scanf("%s", msg->data);  // 获取密码并存储到msg结构体中

	if(send(sockfd, msg, sizeof(MSG),0) < 0)  // 将msg发送给服务器
	{
		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",表示登录成功
	{
		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);  // 获取单词并存储到msg结构体中
		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);  // 将msg发送给服务器
	
	// 接受服务器传递回来的历史记录信息
	while(1)
	{
		recv(sockfd, msg, sizeof(MSG), 0);  // 接收历史记录

		if(msg->data[0] == '\0')  // 如果接收到的历史记录为空
			break;

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

	return 0;  // 返回成功
}

// 主函数,客户端入口
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]);  // 提示正确的使用方法
		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;  // 设置地址族为IPv4
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // 设置服务器IP地址
	serveraddr.sin_port = htons(atoi(argv[2]));  // 设置服务器端口号

	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;  // 返回成功
}

服务器端代码

#include <stdio.h>              
#include <stdlib.h>             
#include <sys/types.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>               

#define   N  32                 // 定义用户名的最大长度

#define  R  1   // 定义操作类型为注册
#define  L  2   // 定义操作类型为登录
#define  Q  3   // 定义操作类型为查询
#define  H  4   // 定义操作类型为历史记录

#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.116.131  6000
int main(int argc, const char *argv[])
{
	int sockfd;  // 套接字描述符
	struct sockaddr_in serveraddr;  // 服务器地址结构体
	int n;  // 菜单选择变量
	MSG msg;  // 信息结构体
	sqlite3 *db;  // 数据库指针
	int acceptfd;  // 接收连接的套接字描述符
	pid_t pid;  // 进程ID

	if(argc != 3)  // 检查命令行参数个数是否正确
	{
		printf("Usage:%s serverip 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");  // 打开数据库成功提示
	}

	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;  // 设置地址族为IPv4
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);  // 设置服务器IP地址
	serveraddr.sin_port = htons(atoi(argv[2]));  // 设置服务器端口号

	if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)  // 绑定套接字
	{
		perror("fail to bind.\n");  // 绑定失败提示
		return -1;  // 返回错误
	}

	// 将套接字设为监听模式
	if(listen(sockfd, 5) < 0)
	{
		printf("fail to listen.\n");  // 监听失败提示
		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");  // 客户端退出提示
	close(acceptfd);  // 关闭与客户端的连接
	exit(0);  // 退出子进程

	return 0;  // 返回成功
}

// 处理注册请求函数
void do_register(int acceptfd, MSG *msg, sqlite3 *db)
{
	char *errmsg;  // 错误信息指针
	char sql[512];  // SQL语句

	snprintf(sql, sizeof(sql), "insert into usr values('%s', '%s');", msg->name, msg->data);  // 构造插入用户信息的SQL语句
	printf("%s\n", sql);  // 打印SQL语句

	if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)  // 执行SQL语句
	{
		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] = {};  // SQL语句
	char *errmsg;  // 错误信息指针
	int nrow;  // 查询结果行数
	int ncloumn;  // 查询结果列数
	char **resultp;  // 查询结果

	snprintf(sql, sizeof(sql), "select * from usr where name = '%s' and pass = '%s';", msg->name, msg->data);  // 构造查询用户信息的SQL语句
	printf("%s\n", sql);  // 打印SQL语句

	if(sqlite3_get_table(db, sql, &resultp, &nrow, &ncloumn, &errmsg) != SQLITE_OK)  // 执行SQL查询
	{
		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;  // 返回成功
	}

	if(nrow == 0)  // 密码或者用户名错误
	{
		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)
	{
		// printf("temp:%s\n", temp);

		// 比较文件中的单词与要查询的单词
		result = strncmp(temp, word, len);

		if(result < 0)
		{
			continue;  // 继续读取下一行
		}
		if(result > 0 || ((result == 0) && (temp[len] != ' ')))
		{
			break;  // 未找到单词,退出循环
		}

		// 表示找到了,查询的单词
		p = temp + len;  // 指向注释部分
		// printf("found word:%s\n", p);
		while(*p == ' ')
		{
			p++;  // 跳过空格
		}

		// 找到了注释,跳跃过所有的空格
		strcpy(msg->data, p);  // 将注释复制到msg结构体中
		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);  // 格式化时间
	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[128] = {};  // SQL语句
	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);  // 构造插入历史记录的SQL语句

		if(sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)  // 执行SQL语句
		{
			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] = {};  // SQL语句
	char *errmsg;  // 错误信息指针

	sprintf(sql, "select * from record where name = '%s'", msg->name);  // 构造查询历史记录的SQL语句

	// 查询数据库
	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;  // 返回成功
}

建立数据库

运行效果

三,QT回顾

udp客户端:

#include "widget.h"
#include <QVBoxLayout>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    te = new QTextEdit;
    le = new QLineEdit;
    pb = new QPushButton("send");
    QVBoxLayout *vbox = new QVBoxLayout;
    vbox->addWidget(te);
    vbox->addWidget(le);
    vbox->addWidget(pb);
    setLayout(vbox);

    udpsock = new QUdpSocket;

    connect(pb, SIGNAL(clicked(bool)), this, SLOT(senddata()));
    connect(udpsock, SIGNAL(readyRead()), this, SLOT(recvdata()));
}

Widget::~Widget()
{

}

udp服务端

#ifndef UDPSERVER_H
#define UDPSERVER_H

#include <QObject>
#include <QUdpSocket>
#include <QDebug>

class udpServer : public QObject
{
    Q_OBJECT
public:
    explicit udpServer(QObject *parent = 0);
    void init()
    {
        udpSocket = new QUdpSocket(this);
        udpSocket->bind(QHostAddress::AnyIPv4, 8888);

        connect(udpSocket, SIGNAL(readyRead()),
                this, SLOT(readPendingDatagrams()));
        qDebug()<<"init....";
    }
signals:

public slots:
    void readPendingDatagrams()
    {
        while (udpSocket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(udpSocket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;

            udpSocket->readDatagram(datagram.data(), datagram.size(),
                                    &sender, &senderPort);

            //processTheDatagram(datagram);
            qDebug()<<"recv: "<<datagram;

            udpSocket->writeDatagram(datagram.data(), datagram.size(),
                                     sender, senderPort);
        }
    }

private:
    QUdpSocket *udpSocket;
};

#endif // UDPSERVER_H

运行效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值