پروژه برنامه نویسی شبکه و ارتباط با سرور در Qt – قسمت دوم
در قسمت قبل رابط گرافیکی برنامه را طراحی کردیم و با شرح کلی پروژه و کلاس های کار با شبکه محیط کیوتی و هدف پروژه آشنا شدیم در این قسمت می خواهیم متدهای برنامه نویسی شبکه را پیاده سازی کنیم و برنامه را کامل کنیم.
همانطور که گفتیم پروتکل ارتباطی ما http و برای مثال آدرس سرور ” http://localhost:8080/Qt-mrzx_ir/server.php ” می باشد.
پیاده سازی متدها:
جهت ارسال درخواست به سرور متدهایی متنوعی وجود دارد ولی پرکاربردین متد های موجود دو متد Get , Post می باشد که از لحاظ امنیتی متد post امنیت نسبتا بالاتری را دارد.
QUrl url(QString("http://localhost:8080/Qt-mrzx_ir/server.php"));
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
QString body="username="+ui->le_uName->text()+"&password="+ui->le_uPass->text();
m_manager->post(req,body.toLatin1());
هنگامی که این درخواست را ارسال کردیم به یک آبجکت از کلاس QNetworkReply نیاز داریم که پاسخ این در خواست را در آن ذخیره کنیم برای این منظور به یک متد(اسلات) نیاز داریم تا وقتی که پاسخ به طور کامل دریافت شد آن متد اجرا و دیتای از جنس QNetworkReply را بخوانیم.
نام این متد را network_reply(QNetworkReply *reply) میگذاریم و سیگنال finished شی m_manager را به آن وصل میکنیم( در کانستراکتور کلاس اصلی).
connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(network_reply(QNetworkReply*)));
لذا پس از ارسال درخواست متد مورد نظز اجرا دیتایش را از طریق آرگومان کلاس مربوطه ارسال میکن و پس اینکه این دیتا را در یافت کردیم لازم است که آن را روی فرم نمایش دهییم.
void zxMain::network_reply(QNetworkReply *reply)
{
if(reply->error()==QNetworkReply::NoError)
QString data=(QString)reply->readAll();
}
reply->error() :
اگر خطایی در هنگام دریافت داده رخ دهد آن خطا را از این متد می توانیم بفهمیم.لذا وقتی خطایی وجود نداشت آن دیتای مورد نظز ما است.
پس تا اینجا ما متوجه شدیم که چگونه یک در خواست را بفرستیم و جگونه پاسخ آن دریافت را بدست بیاوریم.
برنامه سرور:
حال نوبت به برنامه سرور رسیده برای سرور یک برنامه ساده php را درنظر گرفتیم که درصورتیکه نام کاربری و کلمه عبور درست باشد مشخصات بک کاربر را در قالب یک فایل JSON ارسال می کند.
ویکی پدیا: JSON یک استاندارد باز متنی سبک برای انتقال دادهها است به گونهای که برای انسان نیز خوانا باشد. جیسان از زبان اسکریپتنویسی جاوااسکریپت در نشاندادن ساختمان دادههای ساده و آرایههای انجمنی مشتق شدهاست. با وجود ارتباط عمیقی که با جاوااسکریپت دارد، جیسان مستقل از زبان است و مفسرهایش تقریباً برای هر زبانی موجود هستند.
جی سان یک جایگزین بسیار مناسب بجای زبان xml می باشد که خوانایی دیتا را آسانتر میکند.
برای مثال یک دیتای json می تواند مانند زیر باشد:
myObj = {name: "John", age: 31, city: "New York"}
در مثال بالا یک دیتای جی سان با نام myObj داریم که شی name دارای محتوای john و شی عددی age دارای محتوای عدد 31 و … می باشند.
در زبان برنامه نویسی php توابع متعددی برای کار با دیتای جی سان دارد و توان آرایه های این زبان را به داده های جی سان و داده های جی سان را به آرایه هایی تبدیل کرد.
برای این منظور سورس سرور ما به شرح زیر است:
<?php
/*
این یک مثال ساده است برای ارتباط یک نرم افزار در محیط کیوتی با سرور
در این مثال داده ای از جنس دیتای جی سان به کاربر ارسال خواهد شد
https://mrzx.ir
Reza.Ahmadi
*/
header('Content-Type: application/json; charset=utf-8');
if(isset($_POST["username"]) && isset($_POST["password"]))
if(($_POST["username"]=="reza") && ($_POST["password"]=="12345") )
{
$user=array("name"=>"رضا احمدی",
"phone"=>"09379206787",
"brithday"=>"1372/06/21",
"avatar"=>"http://localhost:8080/Qt-mrzx_ir/logo.png",
"error"=>"noError");
$mydata=json_encode($user);
echo $mydata;
}
else
{
$user=array("error"=>"UserOrPass");
$mydata=json_encode($user);
echo $mydata;
}
?>
داده های JSON در Qt :
در Qt برای دسترسی به دیتاهای از نوع جی سان می توانیم از دو کلاس زیر استفاده کنیم:
QJsonDocument
کلاسی است که با استفاده از آن می توانیم داده هایی از نوع json را با فرمت متنی utf-8 بخوانیم و بنویسیم .
QJsonObject
کلاسی است که به ما اجازه دسترسی به اشباء جی سان را می دهد.
پس متد network_reyply() می تواند به شرح زیر باشد :
void zxMain::network_reply(QNetworkReply *reply)
{
if(reply->error()==QNetworkReply::NoError)
{
QString data=(QString)reply->readAll();
QJsonDocument jsonDoc=QJsonDocument::fromJson(data.toUtf8());
m_jsonData=jsonDoc.object();
qDebug()<<data;
if(m_jsonData["error"].toString()=="noError")
{
QString name(m_jsonData["name"].toString()),
phone(m_jsonData["phone"].toString()),
brithday(m_jsonData["brithday"].toString());
QUrl avatarUrl=m_jsonData["avatar"].toString();
send_request(avatarUrl);
}
else
QMessageBox::information(this,"خطا","نام کاربری یا کلمه عبور اشتباه می باشد.");
}
else
QMessageBox::information(this,"خطا",reply->errorString());
}
دقت نمایید که m_jsonData از کلاس QjsonObjectمی باشد .حال ما دیتاهای خود را دریافت کردیم کافی است انها مقادیر آن ها را برای اشیاء درون فرم کیوتی که ایجاد کردیم تنظیم کنیم و به کاربر نمایش دهیم. همانطور که پاسخ سرور مشخص است برای avatar آدرس ان فایل برای ما ارسال شده لذا باید آن فایل را دانلود کرده سپس آن را نمایش دهیم.
دانلود فایل از سرور :
بدین منظور مانند عملیاتی که برای دریافت یک دیتای انجام دادیم اینبار آن دیتا را در یک ابجکت از کلاس QByteArray ذخیره و سپس آن دیتا را با فرمت تصویر (PNG.) ذخیره کنیم و درون یک شی کلاس QPixmap آن را بارگذاری کنیم سپس دفرم مربطه نمایش دهیم.
ابتدا یک شی جدید از کلاس QNetworkAccessManager با نام m_fileDownloader ایجاد میکنیم و آن را به یک متد جهت پایان دانلود با نام downloaded(QNetworkReply *reply) متصل می کنیم:
connect(m_fileDownloader,SIGNAL(finished(QNetworkReply*)),this,SLOT(downloaded(QNetworkReply*)));
متد send_requset(QUrl) آور لود میکنیم ( دوباره پیاده سازی میکنیم) .یعنی ما دو تابع send_request داریم.
void zxMain::send_request(QUrl url)
{
QNetworkRequest req(url);//url of avatar...
m_fileDownloader->get(req);
}
و متد downloaded() به شرح زیر است:
void zxMain::downloaded(QNetworkReply *reply)
{
if(m_file!=NULL)
{
delete m_file;
m_file=NULL;
}
if(reply->error()==QNetworkReply::NoError)
{
m_file=new QByteArray(reply->readAll());
}
show_info();
ui->btn_showInfo->setEnabled(true);
}
ابتدا در کانستراکتور کلاس m_file=NULL قرار دهید. دقت نمایید که m_file از جنس QByteArray می باشد و قرار است در متد show_info() با استفاده از ابجکت m_file و ابجکت m_jsonData مشخصات مورد نظر را به کاربر نمایش دهیم.
نمایش داده ها دریافت شده در فرم :
نکته: هنگامی که کاربر بر روی کلید نمایش (btn_show) کلیک کرد لازم است تا زمانی که دیتا بطور کامل دریافت نشده این کلید غیرفعال باشد و پس از اینکه دیتا دریافت شد آن را مجددا فعال نماییم:
پس بدین منظور در متد btn_shw() ایتدا کد زیر را وارد کنید :
ui->btn_showInfo->setEnabled(false);
و در متدد downloaded() در خط آخر این شی را مجددا فعال کنیم:
ui->btn_showInfo->setEnabled(true);
پیاده سازی متد show_info():
Void zxMain::show_info()
{
ui->lb_name->setText(m_jsonData["name"].toString());
ui->lb_phone->setText(m_jsonData["phone"].toString());
ui->lb_brithday->setText(m_jsonData["brithday"].toString());
QPixmap avatar;
if(m_file->isEmpty())
{
avatar.load(QString(":/img/user.png"));//load default avatar
}
else
{
avatar.loadFromData(*m_file);//load user avatar from m_file...
}
ui->lb_avatar->setPixmap(avatar);
}
در این متد با استفاده از آبجکت m_jsonData رشته های مورد نظر را برای لیبل های موجود در فرم استخراج می کنیم. و عنصر m_file را به یک متغییر از جنس QPixmap پاس می دهیم و سپس متغییر avatar را به عنوان pixmap لیبل lb_avatar تنظیم می کنیم.
چند نکته :
در فرم طراحی شی le_uPass را انتخاب کنید و از پنجره property editor مقدار echoMode را بر روی password قرار دهید.
در کانستراکتور کلاس طول عرض صفحه را تنظیم کنید و کلید ماکسیمایز را غیر فعال کنید:
this->setFixedWidth(400)
this->setFixedHeight(400);
سورس کامل برنامه :
zxMain.h
#ifndef ZXMAIN_H
#define ZXMAIN_H
#include <QMainWindow>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
namespace Ui {
class zxMain;
}
class zxMain : public QMainWindow
{
Q_OBJECT
public:
explicit zxMain(QWidget *parent = 0);
~zxMain();
void show_info();
private:
Ui::zxMain *ui;
QNetworkAccessManager *m_manager;
QNetworkAccessManager *m_fileDownloader;
QJsonObject m_jsonData;
QByteArray *m_file;
//QNetworkReply *m_reply;
void send_request();
void send_request(QUrl url);
private slots:
void btn_show();
void network_reply(QNetworkReply* reply);
void downloaded(QNetworkReply* reply);
};
#endif // ZXMAIN_H
zxmain.cpp
#include "zxmain.h"
#include "ui_zxmain.h"
#include <QMessageBox>
zxMain::zxMain(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::zxMain)
{
ui->setupUi(this);
this->setFixedWidth(400);
this->setFixedHeight(400);
m_manager=new QNetworkAccessManager();
m_fileDownloader=new QNetworkAccessManager();
m_file=NULL;
connect(ui->btn_showInfo,SIGNAL(clicked(bool)),this,SLOT(btn_show()));
connect(m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(network_reply(QNetworkReply*)));
connect(m_fileDownloader,SIGNAL(finished(QNetworkReply*)),this,SLOT(downloaded(QNetworkReply*)));
}
zxMain::~zxMain()
{
delete m_file;
delete m_fileDownloader;
delete m_manager;
delete ui;
}
void zxMain::show_info()
{
ui->lb_name->setText(m_jsonData["name"].toString());
ui->lb_phone->setText(m_jsonData["phone"].toString());
ui->lb_brithday->setText(m_jsonData["brithday"].toString());
QPixmap avatar;
if(m_file->isEmpty())
{
avatar.load(QString(":/img/user.png"));//load default avatar
}
else
{
avatar.loadFromData(*m_file);//load user avatar from m_file...
}
ui->lb_avatar->setPixmap(avatar);
}
void zxMain::send_request()
{
QUrl url(QString("http://localhost:8080/Qt-mrzx_ir/server.php"));
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
QString body="username="+ui->le_uName->text()+"&password="+ui->le_uPass->text();
m_manager->post(req,body.toLatin1());
}
void zxMain::send_request(QUrl url)
{
QNetworkRequest req(url);//url of avatar...
m_fileDownloader->get(req);
}
void zxMain::btn_show()
{
ui->btn_showInfo->setEnabled(false);
send_request();
}
void zxMain::network_reply(QNetworkReply *reply)
{
if(reply->error()==QNetworkReply::NoError)
{
QString data=(QString)reply->readAll();
QJsonDocument jsonDoc=QJsonDocument::fromJson(data.toUtf8());
m_jsonData=jsonDoc.object();
qDebug()<<data;
if(m_jsonData["error"].toString()=="noError")
{
QString name(m_jsonData["name"].toString()),
phone(m_jsonData["phone"].toString()),
brithday(m_jsonData["brithday"].toString());
QUrl avatarUrl=m_jsonData["avatar"].toString();
send_request(avatarUrl);
}
else
QMessageBox::information(this,"خطا","نام کاربری یا کلمه عبور اشتباه می باشد.");
}
else
QMessageBox::information(this,"خطا",reply->errorString());
}
void zxMain::downloaded(QNetworkReply *reply)
{
if(m_file!=NULL)
{
delete m_file;
m_file=NULL;
}
if(reply->error()==QNetworkReply::NoError)
{
m_file=new QByteArray(reply->readAll());
}
show_info();
ui->btn_showInfo->setEnabled(true);
}
دیدگاهتان را بنویسید