- 1 项目简介
- 1.1 概述
- 1.2 开发环境
- 1.3 其他支持
- 1.4 应用界面
- 1.4.1 服务器端
- 1.4.2 客户端
- 1.5 程序使用
- 2 项目开发
- 2.1 搭建基于实验平台数据库
- 2.1.1 数据库详情
- 2.1.2 数据库接口及实现
- 2.1.2.1 用户注册
- 2.1.2.2 用户查询
- 2.2 监控系统客户端及服务端设计
- 2.2.1 `CS`模型
- 2.2.2 功能
- 2.2.2 服务机与客户机交互
- 2.3 客户端设计
- 2.3.1 概述
- 2.3.2 操作界面设计
- 2.3.3 操作流程
- 2.3.4 客户端实现及关键代码
- 2.4 服务端设计
- 2.4.1 概述
- 2.4.2 界面设计
- 2.4.3 实现及关键代码
项目开发报告
)
1 项目简介
1.1 概述
本系统是一个基于
Qt
开发的远程控制监控系统
。采用了C/S
架构,服务端运行在X4418
实验平台上,客户端运行在与实验平台网络通畅的主机上。
本系统分为两大模块:登陆模块
与监控模块
。
服务器端能够通过外置摄像头监控,保存数据库,响应客户端的登陆,注册,截图,暂停等请求。
客户端能够远程访问服务器,在登陆后,进行截屏,暂停等操作。
1.2 开发环境
- 基于Cortex-A9 Android&Linux(s4418)
- X4418实验平台
Qt5.0
以上版本
1.3 其他支持
sqlite
数据库(QSql
)- 内核(课程提供)
1.4 应用界面
1.4.1 服务器端
1.4.2 客户端
1.5 程序使用
- 1 服务器端运行后,此时没有用户登录,
CurrentUser
一栏显示为空。
- 2 启动客户端,按照
192.168.0.101:19999
(默认显示)连接到服务器。
- 3 开启三个页面,分别测试重复注册,登陆空账户,顺利注册。
重复注册
登陆无效账户
顺利注册(test@test)
- 4 此时,用户登录,服务端页面
CurrentUser
显示test
。点击暂停Stop
,暂停屏幕。
- 5 点击截图
ScreenShot
,获取截图。
2 项目开发
2.1 搭建基于实验平台数据库
2.1.1 数据库详情
- Table: User
Item | Description | Type |
UserName | User Account[regist and log] | varchar |
Password | Password | varchar |
- 变量
QSqlDatabase db;
- 数据库初始化
//open database
db = QSqlDatabase::addDatabase("QSQLITE", "QSQLITE");
db.setDatabaseName("./MyDB.db");
bool ok = db.open();
//create table
if (ok) {
QSqlQuery qsQuery = QSqlQuery(db);
QString strSqlText = QString("CREATE TABLE User ( Username VARCHAR(256),Password VARCHAR(256));");
qsQuery.prepare(strSqlText);
if(!qsQuery.exec())
{
QSqlError lastError = qsQuery.lastError();
qDebug() << lastError.driverText() << QString(QObject::tr("Create failed."));
} else {
qDebug() << "Create table user";
}
qsQuery.finish();
}
- 需要支持:
Qt += sql
2.1.2 数据库接口及实现
设计思想:
由类MonitoeServer
来控制,数据库接口只提供增
查
服务。
2.1.2.1 用户注册
- sql语句
INSERT INTO USER (Username, Password)
VALUES (_name, _pw)
void Regist(QString name, QString pw)
//user regist
void MonitorServer::Regist(QString name, QString pw)
{
QSqlQuery qsQuery = QSqlQuery(db);
qsQuery.prepare("INSERT INTO User (Username, Password) "
"VALUES (:name, :pw)");
qsQuery.bindValue(":name", name);
qsQuery.bindValue(":pw", pw);
if(!qsQuery.exec())
{
QSqlError lastError = qsQuery.lastError();
qDebug() << lastError.driverText() << QString(QObject::tr("Insert failed."));
}
qsQuery.finish();
}
2.1.2.2 用户查询
- sql语句
SELECT * FROM USER
WHERE Username = _name
AND Password = _pw
bool ComfirmUser(QString name, QString pw)
//comfirm account exsists or not
bool MonitorServer::ComfirmUser(QString name, QString pw)
{
QSqlQuery qsQuery = QSqlQuery(db);
qsQuery.exec("select * from User WHERE Username = '" + name + "' AND Password = '" + pw + "'");
if(!qsQuery.exec())
{
QSqlError lastError = qsQuery.lastError();
qDebug() << lastError.driverText() << QString(QObject::tr("Query failed."));
}
if (qsQuery.next()) {
qsQuery.finish();
return true;
} else {
qsQuery.finish();
return false;
}
}
2.2 监控系统客户端及服务端设计
2.2.1 CS
模型
采用
Client-Server
模型。
根据需求,将监控系统(实验平台)设置为服务器端[192.168.0.101:19999
],而用户端设置为客户端。
服务器端具有
- 用户数据库
- 监控
而客户端不保留有数据(从服务器端请求的数据除外)。
2.2.2 功能
服务端能够实时监控。
客户端在登陆后能够控制客户端暂停
继续
获取截屏
2.2.2 服务机与客户机交互
- 交互模式
客户端与服务器端[
192.168.0.101:19999
]连接后。每次交互时,客户端向服务器端发送一条请求,服务器端处理后向客户端发送一条响应。
由于过程简单,因此不设定具体形式。
采用tcp
连接。
-
request
response
集合
- 登录请求
request
:login-{UserName}-{Password}-end
response
(success):login-success
response
(fail: No Account):login-fail
- 注册请求
request
:regist
-{UserName}-{Password}-end
response
(success):regist-success
response
(fail: Has Account):regist-fail
- 截屏请求
request
:shot-{UserName}-end-end
response
:Image
- Send two message back, first is response, second is Image(QByteArray)
- 暂停请求
request
:stop-{UserNmae}-end-end
response
(success):stop-success
response
(fail: In Stop State):stop-fail
- 继续请求
request
:continue-{UserName}-end-end
response
(success):continue-success
response
(fail):continue-fail
2.3 客户端设计
2.3.1 概述
为了配合
Server
使用QCamera
,使用QWidget
设计师类。
下面分为操作界面设计
操作流程
实现及关键代码
几个部分进行介绍。
2.3.2 操作界面设计
- 连接到服务器
- 登陆/注册
- 登陆/注册失败
- 主页面
- 总设计图
由于只有两个场景,且每一个场景中的组件数目都不多,因此将所有组件放在一个页面中,通过主类的状态控制及组建的
SetVisible()
方法来控制显示。
2.3.3 操作流程
输入服务器地址及端口号(Initial: 127.0.0.1:19999, Server: 192.168.0.101:19999),并连接到服务器。
输入账号及密码,完成登陆/注册。输入错误,返回上一页面重新输入。
通过按钮控制。(
ScreenShot
: 截屏;Stop/Continue
: 暂停/继续)
Tips:
1. Stop
按钮在点击之后会变成Continue
,再次点击恢复。
2. 点击ScreenShot
按钮后屏幕上会显示当前得到的截屏。
2.3.4 客户端实现及关键代码
- 主类
MonitorClient
class MonitorClient : public QWidget
{
Q_OBJECTpublic:
explicit MonitorClient(QWidget *parent = 0);
~MonitorClient();QTcpSocket *pTcpSocketClient; //socket(client)
QString name; //username
bool getPic; //recieve picture(true), other request
void SendMesg(QString strMesg); //send request to serverprivate:
Ui::MonitorClient *ui;private slots:
//connect to server
void slotConnectServer();//get response from server & deal response
void slotReadMesg();
//log in, send login request
void slotLogin();
//regist, send regist request
void slotRegist();
//return, return to the last layer
void slotReturn();
//screen shot, send screen shot request
void slotShot();
//stop/continue, send stop/ contimue request
void slotStop();};
- 初始化
ui->setupUi(this);
//connect slot
this->connect(ui->pushButton_ConnectServer,SIGNAL(clicked()),this,SLOT(slotConnectServer()));
this->connect(ui->pushButton_login,SIGNAL(clicked()),this,SLOT(slotLogin()));
this->connect(ui->pushButton_regist,SIGNAL(clicked()),this,SLOT(slotRegist()));
this->connect(ui->pushButton_return,SIGNAL(clicked()),this,SLOT(slotReturn()));
this->connect(ui->pushButton_shot,SIGNAL(clicked()),this,SLOT(slotShot()));
this->connect(ui->pushButton_stop,SIGNAL(clicked()),this,SLOT(slotStop()));
//set views
getPic = false;
ui->label_warning->setText("Input the server IP and port");
ui->label_name->setText("IP:");
ui->label_pw->setText("Port:");
ui->lineEdit_name->setText("127.0.0.1");
ui->lineEdit_pw->setText("19999");
ui->splitter_log->setVisible(false);
ui->splitter_screen->setVisible(false);
ui->label_image->setVisible(false);
- 连接服务器
void slotConnectServer()
connect to server
void MonitorClient::slotConnectServer()
{
//get IP and Port
if(ui->lineEdit_name->text().isEmpty() ||ui->lineEdit_pw->text().isEmpty())
{
//Warning
QMessageBox::warning(this,tr("Warning"),tr("Please input IP OR NetPort!"));
return;
}
//abort and reconnect
pTcpSocketClient = new QTcpSocket(this);
pTcpSocketClient->abort();
QString qStrIP = ui->lineEdit_name->text();
quint16 netport = ui->lineEdit_pw->text().toInt();
pTcpSocketClient->connectToHost(qStrIP,netport);
//bind the slot and readyRead
connect(pTcpSocketClient,SIGNAL(readyRead()),this,SLOT(slotReadMesg()));
//set visible
ui->label_warning->clear();
ui->lineEdit_name->clear();
ui->lineEdit_pw->clear();
ui->label_name->setText("UserName:");
ui->label_pw->setText("Password:");
ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
ui->pushButton_ConnectServer->setVisible(false);
ui->splitter_log->setVisible(true);}
- 向服务端发送请求
void SendMesg(QString strMesg)
//send message to server
void MonitorClient::SendMesg(QString strMesg) //发送消息
{
pTcpSocketClient->write(strMesg.toStdString().c_str(),strlen(strMesg.toStdString().c_str()));
}
- 登陆
void slotLogin()
//log in, send login request
void MonitorClient::slotLogin()
{
QString command = "login";
QString name = ui->lineEdit_name->text();
QString pw = ui->lineEdit_pw->text();
QString msg = command + "-" + name + "-" + pw + "-end";
this->SendMesg(msg);
}
- 注册
void slotRegist()
//regist, send regist request
void MonitorClient::slotRegist()
{
QString command = "regist";
QString name = ui->lineEdit_name->text();
QString pw = ui->lineEdit_pw->text();
QString msg = command + "-" + name + "-" + pw + "-end";
this->SendMesg(msg);
}
- 截屏
void slotShot()
//screen shot, send screen shot request
void MonitorClient::slotShot()
{
//need to get Image from server
this->getPic = true;
//set views
QString command = "shot";
QString name = ui->lineEdit_name->text();
QString file = ui->lineEdit_test->text();
QString msg = command + "-" + name + "-" + file + "-end";
this->SendMesg(msg);
ui->label_warning->setText("Screen Shot");
}
- 暂停/继续
void slotStop()
//stop/continue, send stop/ contimue request
void MonitorClient::slotStop() {
if (ui->pushButton_stop->text() == "Stop") {
//set pushButton
ui->pushButton_stop->setText("Continue");
QString command = "stop";
QString name = ui->lineEdit_name->text();
QString msg = command + "-" + name + "-end-end";
this->SendMesg(msg);
} else {
//set pushButton
ui->pushButton_stop->setText("Stop");
QString command = "continue";
QString name = ui->lineEdit_name->text();
QString msg = command + "-" + name + "-end-end";
this->SendMesg(msg);
}
}
- 信息读取及处理
void slotReadMesg()
//get response from server & deal response
void MonitorClient::slotReadMesg()
{
//read the response
QByteArray qba= pTcpSocketClient->readAll();
//if it's a screen shot request
if (getPic) {
//convert QByteArray to Image
QImage *image2 = new QImage;
image2->loadFromData(qba, "PNG");
//show the Image at the label
ui->label_image->setPixmap(QPixmap::fromImage(*image2));
ui->label_image->show();
//set the getImage flag to false
getPic = false;
return;
}
//convert the request to the QString to handle
QString ss=QVariant(qba).toString();
QStringList resp = ss.split("-");
if (resp[0] == "login") {
//login response
if (resp[1] == "success") {
ui->label_warning->setText("Login success");
name = resp[2];
ui->splitter_user->setVisible(false);
ui->splitter_screen->setVisible(true);
ui->label_image->setVisible(true);
} else{
ui->label_warning->setText("Login fail");
ui->splitter_name->setVisible(false);
ui->splitter_pw->setVisible(false);
ui->splitter_log->setVisible(false);
}
} else if (resp[0] == "regist"){
//regist response
if (resp[1] == "success") {
ui->label_warning->setText("Regist success");
name = resp[2];
ui->splitter_user->setVisible(false);
ui->splitter_screen->setVisible(true);
ui->label_image->setVisible(true);
} else{
ui->label_warning->setText("Regist fail");
ui->splitter_name->setVisible(false);
ui->splitter_pw->setVisible(false);
ui->splitter_log->setVisible(false);
}
}
}
2.4 服务端设计
2.4.1 概述
包含数据库及一个摄像头。
启动后,能够相应客户端的请求,客户端能够通过请求控制监控。
QT += multimedia
QT += multimediawidgets
2.4.2 界面设计
2.4.3 实现及关键代码
- 主类
class MonitorServer : public QWidget
{
Q_OBJECTpublic:
explicit MonitorServer(QWidget *parent = 0);
~MonitorServer();
QTcpServer *m_pTcpServer;QTcpSocket *pTcpSocketServer; //Server
QSqlDatabase db; //user account information database
QString user; //current user
//user regist
void Regist(QString name, QString pw);
//comfirm user exsist or not
bool ComfirmUser(QString name, QString pw);private:
Ui::MonitorServer *ui;//send response to client
void SendMesg(QString strMesg);
QCamera* m_pCamera; //load QCamera
QCameraViewfinder* m_pViewfinder; //randered QCamera
QCameraImageCapture* m_pImageCapture; //get QCamera current frameprivate slots:
//new client connect
void slotNewConnect();//get request from client and deal with response
void slotReadMesg();
//test pushButton
void slotTest();};
- 初始化(数据库初始化见上一节)
//initial server
m_pTcpServer = new QTcpServer();
m_pTcpServer->listen(QHostAddress::Any,19999);
this->connect(m_pTcpServer,SIGNAL(newConnection()),this,SLOT(slotNewConnect()));
this->connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(slotTest()));
//initial QCarema
m_pCamera = new QCamera(this);
m_pViewfinder = new QCameraViewfinder(this);
m_pImageCapture = new QCameraImageCapture(m_pCamera);
mainLayout->addWidget(m_pViewfinder);
connect(m_pImageCapture, SIGNAL(imageCaptured(int,QImage)), this, SLOT(cameraImageCaptured(int,QImage)));
m_pImageCapture->setCaptureDestination(QCameraImageCapture::CaptureToFile);
m_pCamera->setCaptureMode(QCamera::CaptureStillImage);
m_pCamera->setViewfinder(m_pViewfinder);
m_pCamera->start();
------
- 处理新的客户端连接
void slotNewConnect()
//new client connect
void MonitorServer::slotNewConnect()
{
pTcpSocketServer = m_pTcpServer->nextPendingConnection();
this->connect(pTcpSocketServer,SIGNAL(readyRead()),this,SLOT(slotReadMesg()));
}
- 用户注册
void Regist(QString name, QString pw)
//user regist
void MonitorServer::Regist(QString name, QString pw)
{
QSqlQuery qsQuery = QSqlQuery(db);
qsQuery.prepare("INSERT INTO User (Username, Password) "
"VALUES (:name, :pw)");
qsQuery.bindValue(":name", name);
qsQuery.bindValue(":pw", pw);
if(!qsQuery.exec())
{
QSqlError lastError = qsQuery.lastError();
qDebug() << lastError.driverText() << QString(QObject::tr("Insert failed."));
}
qsQuery.finish();
}
- 确认用户是否存在
//comfirm account exsists or not
bool MonitorServer::ComfirmUser(QString name, QString pw)
{
QSqlQuery qsQuery = QSqlQuery(db);
qsQuery.exec("select * from User WHERE Username = '" + name + "' AND Password = '" + pw + "'");
if(!qsQuery.exec())
{
QSqlError lastError = qsQuery.lastError();
qDebug() << lastError.driverText() << QString(QObject::tr("Query failed."));
}
if (qsQuery.next()) {
qsQuery.finish();
return true;
} else {
qsQuery.finish();
return false;
}
}
- 发送截屏
void cameraImageCaptured(int, QImage image)
//capture current Image & send response
void MonitorServer::cameraImageCaptured(int, QImage image)
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
QImage *image2 = new QImage;
image2->loadFromData(ba, "PNG");
ui->label->setPixmap(QPixmap::fromImage(*image2));
ui->label->show();
this->SendMesg(QString(ba));
}
- 请求接收及处理
void slotReadMesg()
//get request from client and deal with response
void MonitorServer::slotReadMesg()
{
QByteArray qba= pTcpSocketServer->readAll(); //读取
QString ss=QVariant(qba).toString();
QStringList req = ss.split("-");
if(req[0] == "login") {
qDebug() << "User Log in";
if(this->ComfirmUser(req[1], req[2])) {
qDebug() << "Yes, ok";
QString resp = "login-success-" + req[1] + "-end";
this->SendMesg(resp);
ui->label_user->setText("current user: " + req[1]);
} else{
QString resp = "login-fail-" + req[1] + "-end";
this->SendMesg(resp);
}
} else if (req[0] == "regist") {
qDebug() << "User Regist";
if(this->ComfirmUser(req[1], req[2])) {
qDebug() << "No, ok";
QString resp = "regist-fail-" + req[1] + "-end";
this->SendMesg(resp);
} else{
this->Regist(req[1], req[2]);
ui->label_user->setText("current user: " + req[1]);
QString resp = "regist-success-" + req[1] + "-end";
this->SendMesg(resp);
}
} else if(req[0] == "shot") {
this->m_pImageCapture->capture();
} else if (req[0] == "stop") {
ui->label_user->setText("current user: " + this->user + " STOP");
this->m_pCamera->stop();
} else {
ui->label_user->setText("current user: " + this->user);
this->m_pCamera->start();
}}
</div><div data-report-view="{"mod":"1585297308_001","dest":";}"><div></div></div>
<link href="" rel="stylesheet">
</div>