Я разрабатываю приложение для Raspberry PI на основе интерфейса сокетов. Основная идея заключается в том, что Raspberry будет подключен к датчику, собирать данные и отправлять их через WiFi на устройство Android. С Android я могу общаться с датчиком, отправляя некоторые команды. Я новичок в этом виде разработки и, следуя некоторым учебникам по QTcpSocket, я создал простое клиент-серверное приложение, но оно только в одном направлении. Сервер прослушивает то, что отправляет клиент. Не могли бы вы помочь мне превратить это в двухстороннее общение? Я читал, что QTcpSocket не требует многопоточности для такого рода проблем, но я не нашел никакого решения.
Буду признателен за любую помощь!
server.cpp:
#include "server.h"#include <QTcpServer>
#include <QTcpSocket>
#include <cstdio>
#include <QtDebug>
Server::Server(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(on_newConnection()));
}
void Server::listen()
{
server->listen(QHostAddress::Any, 5100);
}
void Server::on_newConnection()
{
socket = server->nextPendingConnection();
if(socket->state() == QTcpSocket::ConnectedState)
{
printf("New connection established.\n");
qDebug()<<socket->peerAddress();
}
connect(socket, SIGNAL(disconnected()),
this, SLOT(on_disconnected()));
connect(socket, SIGNAL(readyRead()),
this, SLOT(on_readyRead()));
}
void Server::on_readyRead()
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
if(strcmp(ba.constData(), "!exit\n") == 0)
{
socket->disconnectFromHost();
break;
}
printf(">> %s", ba.constData());
}
}
void Server::on_disconnected()
{
printf("Connection disconnected.\n");
disconnect(socket, SIGNAL(disconnected()));
disconnect(socket, SIGNAL(readyRead()));
socket->deleteLater();
}
client.cpp
#include "client.h"#include <QTcpSocket>
#include <QHostAddress>
#include <cstdio>
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
printf("try to connect.\n");
connect(socket, SIGNAL(connected()),
this, SLOT(on_connected()));
}
void Client::on_connected()
{
printf("Connection established.\n");
char buffer[1024];
forever
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
printf("from server: %s", ba.constData());
}
printf(">> ");
gets(buffer);
int len = strlen(buffer);
buffer[len] = '\n';
buffer[len+1] = '\0';
socket->write(buffer);
socket->flush();
}
}
void Client::connectToServer()
{
socket->connectToHost(QHostAddress::LocalHost, 5100);
}
С архитектурной точки зрения вы должны сначала определить некоторые правила связи (поток сообщений) между вашим сервером и клиентом.
Затем просто читайте (записывайте) из (в) экземпляра QTCPSocket в соответствии с заданным потоком.
Вы можете, например, прочитать данные на стороне сервера, проверить, что вы должны ответить, и записать ответ в тот же сокет, из которого вы прочитали. Для сообщений, ориентированных на строки (и только для них), код может выглядеть так:
void Server::on_readyRead()
{
// "while" loop would block until at least one whole line arrived
// I would use "if" instead
if(socket->canReadLine())
{
QByteArray ba = socket->readLine();
QByteArray response;
// some code which parses arrived message
// and prepares response
socket->write(response);
}
//else just wait for more data
}
Лично я бы переместил разбор и отправку ответов из слота on_readyRead (), чтобы избежать слишком длительного блокирования цикла событий, но, поскольку вы — новичок в сетевом программировании, я просто хотел уточнить, что можно сделать для реализации двусторонней связи.
Для более подробной информации вы можете увидеть http://qt-project.org/doc/qt-4.8/qtnetwork.html
Помните о проверке, все ли сообщения поступили как на стороне клиента, так и на стороне сервера. Если вы используете свой собственный протокол (не HTTP, FTP или другой стандартизированный), вы можете добавить длину сообщения в начале сообщения.
Все, что вам нужно сделать, это написать / прочитать из сокета с клиента или сервера. TCP-соединение — это уже двустороннее соединение.
У вас могут быть проблемы с вашим forever
зациклиться Client
, Вы должны действительно использовать readyRead
сигнал на сокете так же, как вы делаете для сервера и сбросить forever
петля.
Обрабатывайте ввод с клавиатуры неблокирующим образом, а не используя gets()
, gets()
заблокирует основной поток и предотвратит запуск цикла обработки событий. Это не лучший способ обработки вещей в вашем случае, так как вы хотите иметь возможность обрабатывать данные с сервера и от пользователя одновременно.
Может быть, взглянем на это в отношении обработки клавиатуры из консольного приложения:
используя-QTextStream для чтения-STDIN-в-неблокируемому-моды
Или сделайте приложение с графическим интерфейсом и используйте QPlainTextEdit
,