基于C/S架构的在线阅读器

项目简介

        本项目实现了用户的基本阅读功能。项目内容涉及到IO,网络编程,C++,QT等知识点。本次项目服务器搭建在ubuntu上,客户端ui在QT中实现,客户端和服务器使用套接字通信。

一、基本功能展示

(1)界面展示

本次项目主要有九个界面,具体结构如下,其中不乏界面的相互跳转,和跨结构跳转

(2)登录注册功能

本项目的登录注册功能除了最基本的保存账号密码,还实现了对登录人的身份判断和登录状态判断。

(3)主界面和个人界面

在主界面中,可以浏览到当前库存的书籍,并且可以通过直接点击书籍,将书籍的名字自动填充到行编辑器中。
在个人界面中,可以进入个人的收藏以及个人的浏览记录
两个界面之间可以相互跳转,在当前界面时无法点击切换到自己界面的按钮

(4)预览界面

在预览界面中,主要展示书籍的信息,并且可以将当前书籍添加到喜欢,也可以直接开始阅读

(5)阅读界面

在阅读界面中,用户可以自己设置字体,可以通过上一页,下一页浏览整本书,可以将文本朗读出来

(6)喜欢和记录界面

在喜欢和记录界面,存放着用户的收藏和历史阅读记录,用户可以通过类似主界面的操作进入阅读界面,阅读自己选中的书籍

(7)管理员界面界面

在管理员啊界面,展示着当前所有的书籍,管理员可以修改书籍的书名以及描述。当管理员选中表中内容时,会自动将表中选中的当前行的内容展示到行编辑器中。底下四个按钮分别可以实现对书籍的增删改查

二、相关原理介绍

(1)数据库结构

        本次项目的数据库结构一共分为三个数据库, 第一个数据库存放着user表和book表,分别存放着用户和书籍的信息,在user表中除了最基本的账号(主键)、密码,还存在这flag和state选项。 flag用于判断登录账号的身份,如果是1则身份为管理员,登录后跳转到管理员界面,如果是0,则为普通用户,跳转到主界面。 state用于判断当前用户是否在线,在登录时会将state置为1,表示已经在线,此时便不可登录,在退出时会置为0,表示可以登录 还有两个数据库分别是“收藏”和“喜欢”数据库,在普通用户登录之后会自动以他们的用户名为名创建表单,分别存放用户收藏的书籍和用户的历史记录

        更新数据库信息代码

int updatebook(sqlite3 *ub,const char *oldbn,const char *newbn,const char *des)
{
	char sql[111]="";
	sprintf(sql,"update book set decribe = \"%s\" where name = \"%s\"",des,oldbn);
	char *errmsg = NULL;
	if(sqlite3_exec(ub,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		printf("delbook error\n");
		sqlite3_free(errmsg);
		return -1;
	}
	bzero(sql,sizeof(sql));
	sprintf(sql,"update book set name = \"%s\" where name = \"%s\"",newbn,oldbn);
	errmsg = NULL;
	if(sqlite3_exec(ub,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		printf("delbook error\n");
		sqlite3_free(errmsg);
		return -1;
	}
	return 0;
}

(2)阅读原理

        本次项目的阅读界面使用了IO的知识,并没有将书籍的内容直接放入数据库,而是以普通文本文件储存,在用户阅读时使用系统IO将数据从文本文件读取出来,传输至客户端。在客户端为了实现翻页的效果,所以需要对传输过来的数据分段。这里使用的是标准模板库中的list容器。在传输过来时,先将内容以两百个字节的大小存入list容器。在阅读界面翻页时只需要清空textbrowser中的数据读取容器中数据显示到组件上就实现了翻页的效果.

        阅读的界面相关代码

void read::preview2read(QString username,QString bookname)
{
    articial.clear();
    ui->textBrowser->clear();
    this->bookname = bookname;
    this->username = username;
    cli.connectToHost(ip,port.toUInt());
    this->show();
    struct msg m;
    strcpy(m.regmsg.flag,"1006");
    QByteArray qbusername = username.toLatin1();
    QByteArray qbbookname = bookname.toLatin1();
    strcpy(m.regmsg.username,qbusername.data());
    strcpy(m.regmsg.password,qbbookname.data());
    cli.write((char *)&m,sizeof(m));
}
void read::slot_readyread()
{
    struct msg m;
    cli.read((char*)&m,sizeof(m));
    QString buf(m.regmsg.password);
    articial.push_back(buf);
    ui->textBrowser->setText(articial.at(0));
}

(3)数据通信原理

        本项目使用结构体作为信息载体,实现了服务器和客户端的数据交换。使用消息标志位判断消息是谁发的,发给谁。

(4)组件通信原理

        本次组件的通信通过信号与槽的连接进行通信。在涉及到界面和界面跳转时需要进行的通信,则将信息存放在信号和槽函数的参数中进行传输。例如,当进入我的收藏界面时我需要我的用户名才能找到我的收藏,所以在登录的时候就将用户名通过界面跳转的信号与槽函数一级一级传输过来

        界面跳转之间的连接代码

    QObject::connect(&w,&Widget::login2register,&r,&reg::login2register);
    QObject::connect(&r,&reg::sig_register2login,&w,&Widget::register2login);
    QObject::connect(&w,&Widget::login2home,&h,&home::login2home);
    QObject::connect(&h,&home::home2preview,&p,&Preview::home2preview);
    QObject::connect(&p,&Preview::preview2read,&re,&read::preview2read);
    QObject::connect(&h,&home::home2mine,&m,&mine::home2mine);
    QObject::connect(&m,&mine::mine2home,&h,&home::mine2home);
    QObject::connect(&m,&mine::mine2favor,&f,&favor::mine2favor);
    QObject::connect(&f,&favor::favor2read,&re,&read::preview2read);
    QObject::connect(&rec,&record::record2read,&re,&read::preview2read);
    QObject::connect(&m,&mine::mine2record,&rec,&record::mine2record);
    QObject::connect(&w,&Widget::login2admin,&ad,&admin::login2admin);

(5)内容展示原理

        本项目使用qtablewidget组件对服务器传来的书本信息进行展示。在服务器端,使用sqlite3_get_table得到列表。由于书本的信息只有书名和描述两种属性,所以在传输过程中,使用结构体存放每一行数据,在客户端接收到对应消息后,增加一行存放接收到的消息数据。

        获取数据库表单内容代码

//获取表单内容
int get_booktable(int sfd,sqlite3 *ub,const char *table_name)
{
	char **res = NULL;
	int rows = 0;
	int cols = 0;
	char *errmsg = NULL;
	char sql[111]="";
	sprintf(sql,"select * from \"%s\"",table_name);
	if(sqlite3_get_table(ub,sql,&res,&rows,&cols,&errmsg)!=SQLITE_OK)
	{
		printf("get table error\n");
		sqlite3_free(errmsg);
		return -1;
	}
	char buf[111]="";
	int i,j;
	for(i=0;i<rows;i++)
	{
		struct msg m;
		strcpy(m.regmsg.flag,"9001");
		strcpy(m.regmsg.username,*(res+(i+1)*cols));
		strcpy(m.regmsg.password,*(res+(i+1)*cols+1));
		send(sfd,&m,sizeof(m),0);
		usleep(50000);
	}
	return 0;
}

        接收并展示到qtablewidget中代码

void home::slot_readyread()
{
    struct msg m;
    cli.read((char*)&m,sizeof(m));
    if(strcmp(m.regmsg.flag,"9001")==0)
    {
        int row = ui->tableWidget->rowCount();
        ui->tableWidget->setRowCount(row + 1);
        QString name(m.regmsg.username);
        QString des(m.regmsg.password);
        QTableWidgetItem *item1 = new QTableWidgetItem(name);
        QTableWidgetItem *item2 = new QTableWidgetItem(des);
        ui->tableWidget->setItem(row,0,item1);
        ui->tableWidget->setItem(row,1,item2);
    }
    if(strcmp(m.regmsg.flag,"9006")==0)
    {
        cli.disconnectFromHost();
        this->close();
    }
}

三、服务器端主函数代码

#include "header.h"
int main(int argc, const char *argv[])
{
	sqlite3 *ub = NULL;
	sqlite3 *favor = NULL;
	sqlite3 *record = NULL;
	if(sqlite3_open("./userandbook.db",&ub)!=SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(ub));
		return -1;
	}
	if(sqlite3_open("./favor.db",&favor)!=SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(ub));
		return -1;
	}
	if(sqlite3_open("./record.db",&record)!=SQLITE_OK)
	{
		printf("%s\n",sqlite3_errmsg(ub));
		return -1;
	}
	sqlite_init(ub,favor,record);//初始化数据库
	
	//创建套接字
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd == -1)
	{
		perror("socket:");
		return -1;
	}
	//端口快速重用
	int reuse = 1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
	{
		perror("setsockaddr error:");
		return -1;
	}




	//绑定信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(SER_PORT);
	sin.sin_addr.s_addr=inet_addr(SER_IP);
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("bind error:");
		return -1;
	}
	//设置监听
	if(listen(sfd,128)==-1)
	{
		perror("listen error:");
		return -1;
	}
	//定义客户端信息结构体
	struct sockaddr_in cin;
	socklen_t socklen=sizeof(cin);
	//IO多路复用
	struct pollfd pfd[1024];
	int n = 1;
	pfd[0].fd=sfd;
	pfd[0].events=POLLIN;
	while(1)
	{
		int res=poll(pfd,n,-1);
		if(res==0)
		{
			printf("manba out\n");
			return -1;
		}
		else if(res==-1&&errno!=4)
		{
			perror("poll error:");
			return -1;
		}
		if(pfd[0].revents==POLLIN)
		{
			int newfd=accept(sfd,(struct sockaddr*)&cin,&socklen);
			pfd[n].fd=newfd;
			pfd[n].events=POLLIN;
			n++;
			printf("[%s:%d:%d]已连接\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
		}
		for(int i = 1;i<n;i++)
		{
			if(pfd[i].revents == POLLIN)
			{
				struct msg m;
				recvfrom(pfd[i].fd,&m,sizeof(m),0,(struct sockaddr*)&cin,&socklen);
				if(strcmp(m.regmsg.flag,"1001")==0)//注册信息
				{
					int res = reg(ub,favor,record,m.regmsg.username,m.regmsg.password);
					if(res==0)
					{
						char buf[111]="";
						strcpy(buf,"success");
						send(pfd[i].fd,buf,sizeof(buf),0);
					}
					else
					{
						char buf[111]="";
						strcpy(buf,"fail");
						send(pfd[i].fd,buf,sizeof(buf),0);
					}
				}
				if(strcmp(m.regmsg.flag,"1002")==0)//登录信息
				{
					int res = login(ub,favor,record,m.regmsg.username,m.regmsg.password);
					char buf[111]="";
					if(res==-2)
					{
						strcpy(buf,"fail1");//用户名不存在
					}
					else if(res==-3)
					{
						strcpy(buf,"fail");//密码错误
					}
					else if(res == -8)
					{
						strcpy(buf,"fail2");//重复登录
					}
					else if(res == 1)
					{
						strcpy(buf,"success1");//管理员登录
					}
					else if(res == 0)
					{
						strcpy(buf,"success");//成功登陆
					}
					send(pfd[i].fd,buf,sizeof(buf),0);
				}
				if(strcmp(m.regmsg.flag,"1003")==0)
				{
					get_booktable(pfd[i].fd,ub,"book");
				}
				if(strcmp(m.regmsg.flag,"1004")==0)
				{
					get_book(pfd[i].fd,ub,m.regmsg.username);
				}
				if(strcmp(m.regmsg.flag,"1005")==0)
				{
					int res = addfavor(ub,favor,m.regmsg.username,m.regmsg.password);
					struct msg m;
					strcpy(m.regmsg.flag,"9003");
					if(res == 0)
					{
						strcpy(m.regmsg.username,"success");
					}
					else if(res == -1)
					{
						strcpy(m.regmsg.username,"fail");
					}
					send(pfd[i].fd,&m,sizeof(m),0);
				}
				if(strcmp(m.regmsg.flag,"1006")==0)
				{
					get_bookcontent(pfd[i].fd,record,m.regmsg.username,m.regmsg.password);
				}
				if(strcmp(m.regmsg.flag,"1007")==0)
				{
					get_favortable(pfd[i].fd,favor,m.regmsg.username);
				}
				if(strcmp(m.regmsg.flag,"1008")==0)
				{
					get_recordtable(pfd[i].fd,record,m.regmsg.username);
				}
				if(strcmp(m.regmsg.flag,"1010")==0)
				{
					int res = userexit(ub,m.regmsg.username);
					struct msg m;
					strcpy(m.regmsg.flag,"9006");
					if(res == 0)
					{
						strcpy(m.regmsg.username,"success");
					}
					else
					{
						strcpy(m.regmsg.username,"fail");
					}
					send(pfd[i].fd,(char *)&m,sizeof(m),0);
				}
				if(strcmp(m.regmsg.flag,"1011")==0)
				{
					get_booklist(pfd[i].fd,ub,"book");
				}
				if(strcmp(m.regmsg.flag,"1012")==0)
				{
					int res = addbook(ub,m.regmsg.username,m.regmsg.password);
					struct msg m;
					strcpy(m.regmsg.flag,"9008");
					if(res == 0)
					{
						strcpy(m.regmsg.username,"success");
					}
					else
					{
						strcpy(m.regmsg.username,"fail");
					}
					send(pfd[i].fd,(char*)&m,sizeof(m),0);
				}
				if(strcmp(m.regmsg.flag,"1013")==0)
				{
					int res = delbook(ub,m.regmsg.username);
					struct msg m;
					strcpy(m.regmsg.flag,"9009");
					if(res == 0)
					{
						strcpy(m.regmsg.username,"success");
					}
					else
					{
						strcpy(m.regmsg.username,"fail");
					}
					send(pfd[i].fd,(char*)&m,sizeof(m),0);
				}
				if(strcmp(m.regmsg.flag,"1014")==0)
				{
					int res = updatebook(ub,m.regmsg.str,m.regmsg.username,m.regmsg.password);
					struct msg m;
					strcpy(m.regmsg.flag,"9010");
					if(res == 0)
					{
						strcpy(m.regmsg.username,"success");
					}
					else
					{
						strcpy(m.regmsg.username,"fail");
					}
					send(pfd[i].fd,(char*)&m,sizeof(m),0);
					
				}
			}
		}
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/437577.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开发利器——C语言必备实用第三方库

​ 对于广大C语言开发者来说&#xff0c;缺乏类似C STL和Boost的库会让开发受制于基础库的匮乏&#xff0c;也因此导致了开发效率的骤降。这也使得例如libevent这类事件库&#xff08;基础组件库&#xff09;一时间大红大紫。 今天&#xff0c;码哥给大家带来一款基础库&#…

【开源物联网平台】FastBee认证方式和MQTT主题设计

&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、接入步骤 1.1 设备认证 1.2 设备交…

思维题(蓝桥杯 填空题 C++)

目录 题目一&#xff1a; ​编辑 代码&#xff1a; 题目二&#xff1a; 代码&#xff1a; 题目三&#xff1a; 代码&#xff1a; 题目四&#xff1a; 代码&#xff1a; 题目五&#xff1a; 代码&#xff1a; 题目六&#xff1a; 代码七&#xff1a; 题目八&#x…

Unity Samples和帧动画的问题

拖动序列帧图片和自己创建clip的帧率不同 我今天在创建帧动画的时候用了两种方式第一种是直接拖动序列帧图片到Hierachy&#xff0c;然后生成的第二种是这样我发现两者播放的动画速率不一样最后查了半天查不到原因。最后发现是Samples的原因&#xff0c;而且Unity把Samples这个…

自动化测试框架、Python面向对象以及POM设计模型简介

1 自动化测试框架概述 所谓的框架其实就是一个解决问题的思维&#xff0c;前言中描述的诸多问题如果得到解决&#xff0c;自然而然这种得到良好解决的东西它就可以称之为一个框架&#xff0c;只是这个框架用来解决如何组织开展自动化测试。一个典型的自动化测试框架一般包括测…

市场复盘总结 20240307

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 89% 最常用的…

[OpenCv]频域处理

目录 前言 一、频域变换 1.傅里叶变换 2.代码实现 二、频域中图像处理 1.理解数字图片的频谱 2.频域图像处理步骤 3.使用低通滤波器实现图像平滑 4.使用高通滤波器实现图像锐化 三、总结 前言 数字图像处理的方法有两大类&#xff1a;一种是空间域处理法&#xff0c;…

【笔记】Android Telephony 漫游SPN显示定制(Roaming Alpha Tag)

一、功能名词简介和显示规则 Alpha Tag&#xff1a;运营商名称标识符&#xff0c;也是用于标识运营商的一个名称。客户需求描述常用名词&#xff0c;对开发而言都是SPN/PLMN功能模块的内容&#xff0c;状态栏左上角的运营商名称显示。 SPN相关文章&#xff1a; 【笔记】SPN和…

nyist_acm 个人积分赛1(部分题解会补充)

Mirrored String II 看到题解说是马拉车算法&#xff0c;我赛时并没想到&#xff08;好吧其实我是比赛完才知道有马拉车这个算法&#xff09; 因为字符串的长度只有1000&#xff0c;直接暴力跑其实就可以了&#xff0c;但是要注意的是&#xff1b;回文串有俩种形式&#xff0c…

nginx部署前端工程替代方案gateway

nginx部署前端工程替代方案gateway 有市场要求部署的前端vue工程不使用nginx中间件。想弄国产替代的东方通之类的&#xff0c;公司没有购买该产品&#xff0c;我参考了网上的一些java网关框架&#xff0c;springcloud组件&#xff1a;gateway实现代替。 注意后台都是用java编…

shadertoy 游戏《来自星尘》摇杆复刻

正确的做法应该是上 noise 而不是叠加 sin 波&#xff0c;不过如果不想麻烦的话叠波还是一个不错的选择&#xff1a;整体效果如下&#xff0c;已经非常形似 直接上链接&#xff1a;Shader - Shadertoy BETA float radiusScale 0.9; float variation(vec2 v1, vec2 v2, float …

据说这是最细滴,Python接口自动化测试数据驱动DDT使用实战,有这一篇就完全足够了

前言 环境准备 首先&#xff0c;需要安装ddt模块 pip install ddt调用时标准格式 在类下面如下写上&#xff1a;ddt.ddt 在调用的方法下面需要写上&#xff1a;ddt.data(需要传入的多组数据) DDT简单介绍 名称&#xff1a; Data-Driven Tests&#xff0c;数据驱动测试。 作用…

深入理解Servlet

目录&#xff1a; ServletWeb开发历史Servlet简介Servlet技术特点Servlet在应用程序中的位置Tomcat运行过程Servlet继承结构Servlet生命周期Servlet处理请求的原理Servlet的作用HttpServletRequest对象HttpServletResponse对象ServletContext对象ServletConfig对象Cookie对象与…

Keepalived实验

一、 LVSKeepalived 实验&#xff1a;7-1为主&#xff1b; 7-2为备&#xff1b; 7-3和7-4为后端服务器 1.关闭防火墙和selinux [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 02.配置主设备7-1 1.安装ipvsadm和keepalived [rootlocalhost ~]#…

阿珊解说Vue中`$route`和`$router`的区别

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

可视化图表:柱坐标系与对应图表详解

一、柱坐标系及其构成 柱状坐标系是一种常见的可视化图表坐标系&#xff0c;用于显示柱状图&#xff08;也称为条形图&#xff09;的数据。它由两个相互垂直的轴组成&#xff0c;一个是水平轴&#xff08;X轴&#xff09;&#xff0c;另一个是垂直轴&#xff08;Y轴&#xff0…

评测本地部署的语音识别模型

1 引言 最近&#xff0c;朋友给我发来了一段音频&#xff0c;想转录成文字&#xff0c;并使用大型润色文本。音频中的普通话带有一定的口音&#xff0c;并且讲解内容较为专业&#xff0c;所以一般的语音识别工具很难达到较高的识别率。 于是试用了两个大模型。Whisper 是目前…

AIOps常见问题

AIOps的自动化通常指什么&#xff1f; AIOps 平台的自动化一般包括以下几个方面&#xff1a; 数据收集和整合&#xff1a;AIOps 平台可以从多个 IT 基础架构组件、应用需求与性能监视工具以及服务工单系统等数据源中收集并整合运维数据&#xff0c;形成一个全面的数据平台。数…

Access AR Foundation 5.1 in Unity 2022

如果已经下载安装了ARF但版本是5.0.7 可以通过下面的方式修改 修改后面的数字会自动更新 更新完成后查看版本 官方文档 Access AR Foundation 5.1 in Unity 2021 | AR Foundation | 5.1.2

YOLOv9中train.py与train_dual.py的异同!

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 首先&#xff0c;train.py&#xff08;左&#xff09;与train_dual.py(右)中的损失函数是不一样的&#xff0c;这也解释了为什么使用train.py除了填入…