基于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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算阶梯数 Python

题目描述 爱因斯坦曾出过这样一道有趣的数学题&#xff1a; 有一个长阶梯&#xff0c; 若每步上2阶&#xff0c;最后剩1阶&#xff1b; 若每步上3阶&#xff0c;最后剩2阶&#xff1b; 若每步上5阶&#xff0c;最后剩4阶&#xff1b; 若每步上6阶&#xff0c;最后剩5阶&#xf…

20240304-使用VS2022编译blender3.6.2源代码

20240304-使用VS2022编译blender3.6.2源代码 一、软件环境 Win10 x64 22h2 JuneVS2022 v17.9.0CMake v3.24.4SVN v1.14.3GIT v2.29.2标签&#xff1a;win10 22h2 vs2022 blender 63335分栏&#xff1a;C 二、硬件环境 Win10 x64的PC台式机 三、获取源码 方法一 网盘下载…

【黑马程序员】C++项目之机房预约管理系统实战

文章目录 需求系统简介身份介绍机房介绍申请简介系统具体需求 实现菜单与退出功能实现功能测试 创建身份类创建角色基类创建学生类创建教师类创建管理员类 登录模块功能描述登录函数封装各个校色具体登录验证管理员操作界面调用流程 管理员模块构造函数实现管理员子菜单显示添加…

设置Matlab2022a断点查看参数变化

Matlab2022a设置断点&#xff0c;查看参数变化 本文使用的是下载好的matlab2022a软件&#xff0c;下载加安装matlab预计1小时&#xff08;百度网盘加速&#xff09;。需要的安装包的评论。 安装好的matlab界面如下&#xff1a; 接下来&#xff0c;编辑一个.m文件&#xff0c;…

简单了解Stable Diffusion里面sampling methods(采样方法)每种方法的效果

在 Stable Diffusion 模型中&#xff0c;采样方法&#xff08;Sampling Methods&#xff09;是指在生成图像时用于从模型的概率分布中抽取样本的算法。这些方法对于生成图像的质量、多样性和速度都有重要影响&#xff0c;以下是一些 Stable Diffusion 中常见的采样方法。 那么…

AI日报:这种病毒从生成式AI工具中窃取您的数据

文章目录 人工智能计算机病毒蠕虫像细菌一样传播病毒测试 人工智能计算机病毒 一组研究人员创造了一种能够利用生成人工智能系统的计算机病毒&#xff0c;包括Gemini Pro和GPT-4驱动的ChatGPT版本。 Morris II是一种蠕虫&#xff0c;它操纵生成的人工智能模型来执行恶意任务&…

模拟实现std::string类(包含完整、分文件程序)

std库中的string是一个类&#xff0c;对string的模拟实现&#xff0c;既可以复习类的特性&#xff0c;也可以加深对std::string的理解。 &#x1f308;一、搭建框架 ☀️1.新命名空间 本质上string是一个储存在库std里面的类&#xff0c;现在需要模拟实现一个string类&#…

Chatgpt 3.5简单使用教程,邮箱不能使用、账户不能使用等问题

官网能连接必要条件。当然国内的人工智能也不错 &#xff1a;百度一下&#xff0c; 文心一言和通义官网 一开始注册&#xff0c;登录一直报错&#xff0c;导致我很烦。 注册一直邮箱或者账户不能使用不能使用&#xff0c;建议使用浏览器无痕模式&#xff0c;实在不行&#xf…

内衣洗衣机怎么选?2024年度最新爆品内衣洗衣机测评

内衣裤洗衣机是一种非常实用的洗衣机&#xff0c;可以有效地保护内衣和贴身衣物的质量和卫生&#xff0c;相比于普通的家用大型洗衣机&#xff0c;内衣裤洗衣机在容量、洗涤方式、控制方式和价格等方面有很大的不同之处&#xff0c;如果您经常需要清洗内衣和贴身衣物&#xff0…

吴恩达deeplearning.ai:数据增强数据合成迁移学习

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 让我们看看为你的程序添加数据的技巧。在构建神经网络的时候&#xff0c;我们总是想要更多的数据&#xff0c;但是获取更多的数据往往是十分昂贵又缓慢的。相反地&#xff0c;添加数据的另一…

Dockerfile(6) - EXPOSE 指令详解

EXPOSE 通知 Docker 容器在运行时监听指定的网络端口 EXPOSE 端口号 EXPOSE 端口号/协议 默认协议是 TCP 同时在 TCP、UDP 上暴露端口 EXPOSE 80/tcp EXPOSE 80/udp EXPOSE 原理 个人理解&#xff1a;EXPOSE 暴露的端口更像是指明了该容器提供的服务需要用到的端口EXPOSE…

蓝桥杯2017年第八届真题-分巧克力

目录 题目描述 输入格式 输出格式 样例输入 样例输出 原题链接 代码实现 题目描述 儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。 小明一共有N块巧克力&#xff0c;其中第i块是Hi x Wi的方格组成的长方形。 为了公平起见&#xff0c;小明需…

如何加速访问NexusMods网站

下载Steam(Watt Toolkit) 到steam官网下载https://steampp.net/ 使用Steam加速Nexus 打开Steam点击网络加速&#xff0c;点击平台加速&#xff0c;勾选Nexus Mods&#xff0c;以及下面的所有子项&#xff0c;如何点击一键加速。 注意事项 如果加速效果不好可以尝试切换加…

大数据和机器学习在气象预报中的应用-张平文院士

报告链接&#xff1a;张平文院士 -- 大数据和机器学习在气象预报中的应用_哔哩哔哩_bilibili

Ansible 基础入门

2&#xff09;Ansible 介绍 Ansible 基本概念 Ansible 是一种自动化运维工具&#xff0c;基于 Paramiko 开发的&#xff0c;并且基于模块化工作&#xff0c;Ansible 是一种集成 IT 系统的配置管理、应用部署、执行特定任务的开源平台&#xff0c;它是基于 Python 语言&#xf…

CXYGZL实现钉钉、飞书和微信全面覆盖!!!

非常欣慰能在这里与大家分享&#xff0c;CXYGZL已圆满实现多端互通的目标&#xff01;&#xff01;&#xff01; 无论您是在手机、电脑还是平板上使用钉钉、企微还是飞书&#xff0c;只需将CXYGZL轻松集成到您的办公软件中&#xff0c;即可实现无缝审批处理各项任务&#xff0c…

Polar 写shell

Polar 写shell 直接给了源码 还是没啥好说的&#xff0c;考点是die()死亡函数绕过之不同变量 **绕过原理&#xff1a; **通过base64解密或rot13解密使"<?php exit();"变为乱码&#xff0c;而传入的$content为base64编码&#xff0c;解码后为正常shell语句。通过…

Java面试篇【并发编程】常见面试题(2024最新)

Java并发编程常见面试题 1.什么是线程和进程&#xff1f; 进程是操作系统分配资源的最小单位&#xff0c;各个进程之间占据独立的寻址空间&#xff0c;运行也是独立运行&#xff0c;进程间通信需要一些机制。进程间切换需要的开销较大。 线程是程序执行的基本单位&#xff0c…

聊一聊ThreadLocal的原理?

1.ThreadLocal创建方式 ThreadLocal<String> threadlocal1 new ThreadLocal(); ThreadLocal<String> threadlocal2 new ThreadLocal(); ThreadLocal<String> threadlocal3 new ThreadLocal(); 2.首先介绍一下&#xff0c;ThreadLocal的原理&#xff1a; 如…

Qt+Opencv实现视频二维码检测

编译器&#xff1a;Qt 5.12.2Visual Studio 2015 视觉库&#xff1a;OpenCV 4.5.5 能够加载MP4格式视频、读取、定位二维码并输出与反馈信息的软件 需要演示Demo可私信