qt network explained (portuguese)
DESCRIPTION
Desenvolver uma aplicação que precisa de comunicar com a rede não precisa ser complicado. Neste webinar será mostrado como o Qt torna programação para rede muito mais simples. Será dado um rápido overview de como criar sockets UDP e TCP, broadcast e multicast além de fazer requisições REST usando somente a API do QtNetwork.TRANSCRIPT
1 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
ECOSYSTEM
QtNetwork explained
2 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
TÓPICOS
3 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
• Introdução ao QtNetwork
• Por que usar QtNetwork
• Sockets● QAbstractSocket● QTcpSocket
– QsslSocket● QTcpServer● QudpSocket
– Broadcast– Multicast (somente 4.8 !symbian)
TÓPICOS
4 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
• High level networking● QnetworkAccessManager● QnetworkConfiguration● QnetworkConfigurationManager● QnetworkSession● QnetworkRequest● QNetworkReply
Introdução ao QtNetwork• QtNetwork é um módulo do Qt
• Provê uma interface cross platforma para escrever aplicações
cliente servidor TCP/IP
• Suporta proxy
• Possui classes para realizar requests http e ftp, acessar web
services e tratar suas respostas.
• Pegar informações sobre dispositivos de rede
• É possível ainda gerenciar o estado de conexão com a rede, ter
configurações específicas para cada uma e realizar ações.
Por que usar QtNetwork?int main(){ int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == 1) { printf("can not create socket"); exit(1); } struct sockaddr_in saddr; bzero(&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(1100); saddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(fd,(struct sockaddr *)&saddr, sizeof(saddr)) == 1) { printf("error bind failed"); exit(1); }
if (listen(fd, 10) == 1) { printf("error listen failed"); exit(1); } for(;;) { int connfd = accept(fd, NULL, NULL); if(connfd <= 0) { printf("error accept failed"); exit(1); }
write(connfd, "Hello world!\n", 13); close(connfd); }
return 0;}
int main(){ QCoreApplication app(argc, argv);
QTcpServer server; server.listen(QHostAddress::Any, 1100);
while (server.waitForNewConnection()) { do { QTcpSocket *socket = server.nextPendingConnection(); socket->write(“Hello, world!”); socket->disconnectFromHost(); socket->waitForDisconnected(); delete socket; } while (server.hasPendingConnections()); }}
Sockets: QAbstractSocket
7 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
• QabstractSocket é uma camada de abstração multi plataforma para api nativa de network
• Implementa TCP, UDP e sockets locais• Herda de QIODevice
Sockets: QTcpSocket● Subclasse de conveniência do QAbstractSocket
● Pode ser utilizado pra implementar vários tipos protocolos
● Stream de dados contínuo
● Poder ser usado de maneira asíncrona ou síncrona.
Sockets: QTcpSocketServerSocket::ServerSocket(QObject *parent) : QTcpSocket(parent){ connect(this, SIGNAL(readyRead()), this, SLOT(onReadyRead()));}
void ServerSocket::onServerInfoResponse(const QVariantMap &map){ QJson::Serializer serializer;
write(serializer.serialize(map));
flush();}
void ServerSocket::onServerStatusChanged(const QString &status) { QVariantMap map; map["what"] = "server"; map["action"] = "statusChanged"; map["status"] = status;
QJson::Serializer serializer; write(serializer.serialize(map)); flush();}
void ServerSocket::onReadyRead(){ while (canReadLine()) { QByteArray line = readLine();
bool parserOk = true; QJson::Parser parser; QVariant var = parser.parse(line, &parserOk);
if (!parserOk) { qWarning() << Q_FUNC_INFO << "json error at" << parser.errorLine() << parser.errorString(); } else { QVariantMap map = var.toMap(); QString what = map["what"].toString(); if (what == "server") { QString action = map["action"].toString(); if (action == "info") { emit serverInfoRequested(); } } } }}
ClientSocket::ClientSocket(QObject *parent) : QTcpSocket(parent){ connect(this, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(this, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));}
void ClientSocket::onDisconnected(){ qDebug() << "[ClientSocket] Client disconnected!";}
void ClientSocket::onError(QAbstractSocket::SocketError error){ qDebug() << "[ClientSocket] Error: " << error;}
void ClientSocket::requestServerInfo(){ QVariantMap map; QJson::Serializer serializer;
map["what"] = "server"; map["action"] = "serverInfo";
write(serializer.serialize(map)");}void ClientSocket::onQJsonParsingFinished(const QVariant &json, bool ok, const QString &error_msg){ if (!ok) { qWarning() << Q_FUNC_INFO << "json error at" << error_msg; return; }
QVariantMap map = json.toMap(); QString what = map["what"].toString(); if (what == "server") { QString action = map["action"].toString(); if (action == "serverInfo") { emit serverInfoReceived(map); } }}
Sockets: QTcpServer● Precisa receber conexões? Essa é a classe certa.
● Pode ser single threaded or multi threaded
● Single threaded● Um ou vários sockets usando mesmo Event loop
● Ou Bloqueante
● Multi threaded● Um socket por thread
– Bloqueante
– Ou um Event loop por thread
Sockets: QTcpServerServer::Server(QObject *parent) : QtcpServer(parent) , m_serverInfo(){}
void Server::start(){ listen(QHostAddress::Any, 12345);}
void Server::incomingConnection(int socketDescriptor) { ServerSocket *sock = new ServerSocket; sock->setSocketDescriptor(socketDescriptor); QThread *thread = new QThread; sock->moveToThread(thread);
connect(sock, SIGNAL(disconnected()), sock, SLOT(deleteLater())); connect(sock, SIGNAL(destroyed()), thread, SLOT(quit())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(this, SIGNAL(statusChanged(const Qstring &)), sock, SLOT(onServerStatusChanged(const Qstring &)));
connect(sock, SIGNAL(serverInfoRequested()), this, SLOT(onServerInfoRequested()));
thread->start();}
void Server::onServerInfoRequested(){ ServerSocket *sock = qobject_cast<ServerSocket*>(sender()); if (!sock) return;
QMetaObject::invokeMethod(sock, "onServerInfoResponse", Q_ARG(QVariantMap, m_serverInfo));}
Sockets: QudpSocket BroadcastBroadcastAnnounce::BroadcastAnnounce(QObject *parent)
: QObject(parent)
, m_socket(new QUdpSocket(this))
, m_timer(new QTimer(this))
, m_serverInfo()
{
connect(m_timer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
}
void BroadcastAnnounce::start()
{
if (m_timer->isActive())
m_timer->stop();
m_timer->start(1000);
}
void BroadcastAnnounce::stop()
{
m_timer->stop();
}
void BroadcastAnnounce::setServerInfo(const QVariantMap &map)
{
m_serverInfo = map;
}
void BroadcastAnnounce::sendDatagram()
{
QByteArray datagram;
QDataStream out(&datagram, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << QVariant(m_serverInfo);
m_socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, UDP_PORT);
}
Sockets: QudpSocket BroadcastBroadcastAnnounce::BroadcastAnnounce(QObject *parent)
: QObject(parent)
, m_socket(new QUdpSocket(this))
, m_timer(new QTimer(this))
, m_serverInfo()
{
connect(m_timer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
}
void BroadcastAnnounce::start()
{
if (m_timer->isActive())
m_timer->stop();
m_timer->start(1000);
}
void BroadcastAnnounce::stop()
{
m_timer->stop();
}
void BroadcastAnnounce::setServerInfo(const QVariantMap &map)
{
m_serverInfo = map;
}
void BroadcastAnnounce::sendDatagram()
{
QByteArray datagram;
QDataStream out(&datagram, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << QVariant(m_serverInfo);
m_socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::Broadcast, UDP_PORT);
}
BroadcastDiscover::BroadcastDiscover(QObject *parent) : QObject(parent) , m_socket() , m_timer(new QTimer(this)){ connect(m_timer, SIGNAL(timeout()), this, SLOT(stop()));}
void BroadcastDiscover::start(){ m_socket = new QUdpSocket(this); m_socket->bind(UDP_PORT, QUdpSocket::ShareAddress); connect(m_socket, SIGNAL(readyRead()), this, SLOT(processDatagrams())); m_timer->start(10000);}
void BroadcastDiscover::stop(){ disconnect(m_socket, SIGNAL(readyRead()), this, SLOT(processDatagrams())); m_socket->close(); emit timeout();}
void BroadcastDiscover::processDatagrams(){ QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender()); if (!socket) return;
while (socket->hasPendingDatagrams()) { QByteArray datagram; QHostAddress host;
datagram.resize(socket->pendingDatagramSize()); socket->readDatagram(datagram.data(), datagram.size(), &host);
QVariant message; QDataStream dataStream(&datagram, QIODevice::ReadOnly); dataStream >> message;
const QVariantMap map = message.toMap(); if (map["app_id"].toString() == APP_ID) { const QString action = map["action"].toString(); if (action == "serverAnnounce") { emit serverFound(map); } } }}
Sockets: QudpSocket MulticastMulticastAnnounce::MulticastAnnounce(QObject *parent) : QObject(parent) , m_socket(new QUdpSocket(this)) , m_timer(new QTimer(this)) , m_serverInfo(){ connect(m_timer, SIGNAL(timeout()), this, SLOT(sendDatagram()));
m_socket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);}
void MulticastAnnounce::start(){ if (m_timer->isActive()) m_timer->stop();
m_timer->start(1000);}
void MulticastAnnounce::stop(){ m_timer->stop();}
void MulticastAnnounce::setServerInfo(const QVariantMap &map){ m_serverInfo = map;}
void MulticastAnnounce::sendDatagram() { QByteArray datagram; QDataStream out(&datagram, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_8); out << QVariant(m_serverInfo);
m_socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress("239.255.43.21"), UDP_PORT);}
MulticastDiscover::MulticastDiscover(QObject *parent) : QObject(parent) , m_socket() , m_timer(new QTimer(this)){ connect(m_timer, SIGNAL(timeout()), this, SLOT(stop()));}
void MulticastDiscover::start(){ m_socket = new QUdpSocket(this); m_socket->bind(UDP_PORT, QUdpSocket::ShareAddress); m_socket->joinMulticastGroup(QHostAddress("239.255.43.21"));
connect(m_socket, SIGNAL(readyRead()), this, SLOT(processDatagrams())); m_timer->start(10000);}
void MulticastDiscover::stop(){ disconnect(m_socket, SIGNAL(readyRead()), this, SLOT(processDatagrams())); m_socket->close(); emit timeout();}
void MulticastDiscover::processDatagrams(){ QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender()); if (!socket) return;
while (socket->hasPendingDatagrams()) { QByteArray datagram; QHostAddress host;
datagram.resize(socket->pendingDatagramSize()); socket->readDatagram(datagram.data(), datagram.size(), &host);
QVariant message; QDataStream dataStream(&datagram, QIODevice::ReadOnly); dataStream >> message;
const QVariantMap map = message.toMap(); if (map["app_id"].toString() == APP_ID) { const QString action = map["action"].toString(); if (action == "serverAnnounce") { emit serverFound(map); } } }}
Sockets: QUdpSocket● Subclasse de conveniência do QabstractSocket para
UDP
● Envio de pacotes de dados ao invés de stream contínuo
● Possui suporte a Broadcast
● Desde o Qt 4.8 suporte a Multicast
QnetworkConfigurationManager
16 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
1- Definir o Ponto de Acesso
QNetworkConfigurationManager manager;
//Define que o usuário pode escolher um ponto de acesso
const bool canStartIAP = (manager.capabilities()& QnetworkConfigurationManager::CanStartAndStopInterfaces);
//Retorna a configuração padrão atual
manager.defaultConfiguration();
QnetworkConfiguration
17 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
1- Definir o Ponto de Acesso
QNetworkConfigurationManager manager;
//Define que o usuário pode escolher um ponto de acesso
const bool canStartIAP = (manager.capabilities()& QNetworkConfigurationManager::CanStartAndStopInterfaces);
//Se existe um ponto de acesso padrao. Basta utiliza-lo usando a configuracao padrao do QNetWorkConfigurarionManager
QNetworkConfiguration cfg = manager.defaultConfiguration();
if (!cfg.isValid() || !canStartIAP) {
// Pontos de acesso nao encontrados ou impossivel se conectar
// Alertar usuario e fazer tratamento de erro
return;
}
QnetworkSession
18 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
// Caso a conexao tenha ocorrido com sucesso, abrir um QNetworkSession usando a configuracao padrao
m_session = new QNetworkSession(cfg);
//Conectar sinais necessarios
connect(m_session, SIGNAL(closed()), this, SLOT(closed()));
connect(m_session, SIGNAL(stateChanged(QNetworkSession::State)), this, SLOT(stateChanged(QNetworkSession::State)));
connect(m_session, SIGNAL(error(QNetworkSession::SessionError)), this, SLOT(error(QNetworkSession::SessionError)));
Abrir aconexao
m_session->open();
// Espera a sessão ser aberta e continua a partir dai
m_session->waitForOpened();
Para fechar a sessão, usar o slot finished()
QnetworkAccessManager
19 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
1- Criar um Network Access Manager
nam = new QNetworkAccessManager(this);
2- Conectar o sinal finished a um slot para tratar o resultado
QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(finishedSlot(QNetworkReply*)));
QnetworkRequest
20 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
1- Criar um Network Access Manager
nam = new QNetworkAccessManager(this);
2- Conectar o sinal finished a um slot para tratar o resultado
QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(finishedSlot(QNetworkReply*)));
3- Criar o Request
QUrl url(http://query.yahooapis.com/v1/public/yql);
url.addQueryItem(QUERY_PARAM,
QLatin1String("select+*+from+geo.placefinder+where+text=\"")
+ "Sao Paulo" + QLatin1String("\""));
url.addQueryItem("format", "json");
QnetworkRequest request(url);
QNetworkReply
21 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
1- Criar um Network Access Manager
nam = new QNetworkAccessManager(this);
2- Conectar o sinal finished a um slot para tratar o resultado
QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(finishedSlot(QNetworkReply*)));
3- Criar o Request
QUrl url(http://query.yahooapis.com/v1/public/yql);
url.addQueryItem(QUERY_PARAM,
QLatin1String("select+*+from+geo.placefinder+where+text=\"")
+ "Sao Paulo" + QLatin1String("\""));
url.addQueryItem("format", "json");
QnetworkRequest request(url);
4- Guardar o reply do request par ser usado como identificador da resposta
QNetworkReply* reply = nam->get(request)
QNetworkReply
22 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
4- Tratar o Reply
void MyHttpEngine::finishedSlot(QNetworkReply* reply) {
// Ler Atributos do Repply como ocodigo de Status HTTP
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
void MyHttpEngine::finishedSlot(QNetworkReply* reply) {
// Ler Atributos do Repply como o codigo de Status HTTP
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
// Ou a URL, se esta tiver sido redirecionada:
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
// Verificar se houve erro
if (reply->error() == QnetworkReply::NoError) {
// Se nao houve erro, ler os dados da resposta
//Ex1: Criando um QImage da resposta
QImageReader imageReader(reply);
QImage pic = imageReader.read();
}
}
Canais de comunicação
23 INdT 2012 | Filename.pptx v. 0.1 YYYY-MM-DD Author Document ID [Edit via Insert > Header & Footer]
• @nokiadev_brasil• [email protected]