基于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.hqwc.cn/news/519272.html

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

相关文章

力扣513 找树左下角的值 Java版本

文章目录 题目描述解题思路代码 题目描述 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出: 7 提示: 二…

python 截取字符串string.split

目录 作用语法只要第一个值获得第3个值遍历 作用 根据某个符号对数据进行截取 从而获得自己想要的内容 语法 使用’string.split’ 方法 对字符串’123/abc/BPYC’ 以 ‘/’ 进行截取 string "123/abc/BPYC" substring string.split("/") print(subs…

cocos creator 3.7.2使用shader实现图片扫光特效

简介 功能:图片实现扫光效果 引擎:cocos Creator 3.7.2 开发语言:ts 完整版链接 链接https://lengmo714.top/284d90f4.html 效果图 shader代码 // Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd. CCEffect %{techniques:- passes:- vert: sprite-vs:vertfrag…

MySQL 备份方案

优质博文&#xff1a;IT-BLOG-CN 一、为什么要备份 【1】容灾恢复&#xff1a;硬件故障、不经意的 Bug 导致数据损坏&#xff0c;或者服务器及其数据由于某些原因不可获取或无法使用等&#xff08;例如&#xff1a;机房大楼烧毁&#xff0c;恶意的黑客攻击或 Mysql 的 Bug 等&…

Linux:线程控制和原生线程库

文章目录 线程的id和LWP线程的终止线程的返回值问题关于原生线程库问题 本篇总结的内容主要是关于线程的控制专题 线程的id和LWP 对于获取线程的id来说&#xff0c;在Linux系统中存在这样的调用 这个调用就可以获取返回当前线程的id 先写出下面的实例代码 #include <ios…

基于springboot+vue的高校教师电子名片系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

OJ习题之——圆括号编码

圆括号编码 1.题目描述2.完整代码3.图例演示 1.题目描述 题目描述 令Ss1 s2 …sn是一个规则的圆括号字符串。S以2种不同形式编码&#xff1a; &#xff08;1&#xff09;用一个整数序列Pp1 p2 … pn编码&#xff0c;pi代表在S中第i个右圆括号的左圆括号数量。&#xff08;记为…

进程之舞:操作系统中的启动、状态转换与唤醒艺术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

nmaptocsv.py脚本无法处理结果的备用工具

python nmaptocsv.py -i test.nmap -f ip-fqdn-port-protocol-service-version。 一: 使用背景&#xff1a; 使用一些其他的端口扫描软件&#xff0c;指纹识别可能有些端口 如一次&#xff1a;11011端口是mysql数据库但是别的软件扫不出来是mysql&#xff0c;借助nmap对1101…

基类指针指向派生类对象,基类不带虚函数,子类带虚函数产生的异常分析

基类指针指向派生类对象&#xff0c;基类不带虚函数&#xff0c;子类带虚函数产生的异常分析 基类指针指向派生类对象&#xff0c;指针的起始地址一定是指向基类起始地址的 这种情况下&#xff0c;当基类没有虚函数&#xff0c;而子类存在虚函数时&#xff0c;就会出现问题&am…

lqb省赛日志[1/37]

一只小蒟蒻备考蓝桥杯的日志 文章目录 笔记mod进制转换轻轻崩溃前缀和 差分双指针&#xff08;夹逼&#xff09;人脑思维...->计算机思维历史遗留问题1--22年B组初赛历史遗留问题2--试题 基础练习 阶乘计算 刷题心得小结 笔记 mod 进制转换 进制转换——颠覆小蒟蒻认知了…

Java开发从入门到精通(一):Java的基础语法高阶

Java大数据开发和安全开发 &#xff08;一)Java的流程控制1.1 分支语句1.1.1 IF分支语句第一种IF语句第二种IF-ELSE语句第三种IF-ELSE IF-ELSE语句if语句使用的几个常见问题 1.1.2 switch分支语句switch分支的执行流程switch分支的导学案例:电子备忘录if、switch的比较&#xf…