項目開發報告
項目開發報告
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_OBJECT
public:
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 server
private:
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_OBJECT
public:
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 frame
private 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();
}
}