Как написать клиент-серверное приложение и реализовать простой протокол в Qt

Может быть, это глупый вопрос, на самом деле это обращение, или Qt просто сложен для меня.
Вот в чем дело:
Я привык к Java при написании клиент-серверных приложений, и это очень просто. Я хотел бы делать то же самое в C ++ (я очень хорошо знаком с самим C ++), и я решил изучать Qt. Я пытался написать несколько приложений на qt, но с частичным успехом.

Первое, что меня беспокоит, это сигналы и слоты. Я знаю, как использовать их в программировании GUI, но это путает меня с сетью. И есть проблема с блокировкой. Когда я вызываю метод readLine () в BufferedReader в java, он блокируется, пока не получит строку от соединения с сокетом. В Qt я должен убедиться, что линия доступна каждый раз, и обрабатывать ее, когда ее нет.

И когда я подключаю сигнал ошибки QSocket к некоторым из моих пользовательских слотов, сигнал испускается, когда сервер отправляет последнюю строку и закрывает соединение, и в слоте / функции клиента, который читает, я никогда не читаю эту последнюю строку. Вот некоторые проблемы, с которыми я столкнулся до сих пор.

Слоты и проверка наличия данных приводят меня в замешательство, когда мне приходится реализовывать даже самые простые протоколы.

Важная часть:

Я пытался найти хороший пример в интернете, но проблема в том, что все примеры слишком сложны. Есть ли кто-нибудь, как может показать мне, как написать простое клиент-серверное приложение. Сервер принимает только одного клиента. Клиент отправляет текстовую строку, содержащую команду. Если команда «ADD» или «SUB», сервер отправляет «SUP», указывая, что команда поддерживается. В противном случае он отправляет «UNS» и закрывает соединение. Если клиент получает «SUP», он отправляет больше строк, содержащих числа, которые будут вычтены или добавлены. Сервер отвечает результатом и закрывает соединение.

Я знаю, что C ++ требует больше кодирования, но в Java это займет всего 5 минут, поэтому на его написание на C ++ тоже не нужно много времени.

Я уверен, что этот пример будет очень ценным для всех, кто хочет научиться работать в сети на Qt.

редактировать:
Это моя попытка сделать приложение (описано выше):
вот серверная часть:

 #ifndef TASK_H
#define TASK_H

#include <QObject>
#include <QTcpServer>

class Task : public QObject
{
Q_OBJECT
public:
Task(QObject *parent = 0) : QObject(parent) {}public slots:
void run();
void on_newConnection();
void on_error(QAbstractSocket::SocketError);

signals:
void finished();

private:
QTcpServer server;
};#endif // TASK_Hvoid Task::run()
{
connect(&server,SIGNAL(newConnection()),this,SLOT(on_newConnection()));
connect(&server,SIGNAL(acceptError(QAbstractSocket::SocketError)),this,SLOT(on_error(QAbstractSocket::SocketError)));
if(server.listen(QHostAddress::LocalHost, 9000)){
qDebug() << "listening";
}else{
qDebug() << "cannot listen";
qDebug() << server.errorString();
}

}

void Task::on_newConnection(){
std::cout << "handeling new connection...\n";
QTcpSocket* socket = server.nextPendingConnection();
QTextStream tstream(socket);while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}

QString operation = tstream.readLine();
qDebug() << "dbg:" << operation;
if(operation != "ADD" && operation != "SUB"){
tstream << "UNS\n";
tstream.flush();
socket->disconnect();
return;
}
tstream << "SUP\n";
tstream.flush();
double op1,op2;
while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}
op1 = socket->readLine().trimmed().toDouble();
qDebug() << "op1:" << op1;
while(!socket->canReadLine()){
socket->waitForReadyRead(-1);
}
op2 = socket->readLine().trimmed().toDouble();
qDebug() << "op2:" << op2;
double r;
if(operation == "ADD"){
r = op1 + op2;
}else{
r = op1 - op2;
}

tstream << r << "\n";
tstream.flush();
qDebug() << "result is: " << r;
socket->disconnect();

}

void Task::on_error(QAbstractSocket::SocketError ){
qDebug() << "server error";
server.close();
}

Это на стороне клиента (заголовок аналогичен заголовку сервера, поэтому я не буду его публиковать):

void Task::run()
{QTcpSocket socket;
std::string temp;
socket.connectToHost(QHostAddress::LocalHost,9000);

if(socket.waitForConnected(-1))
qDebug() << "connected";
else {
qDebug() << "cannot connect";
return;
}

QTextStream tstream(&socket);

QString op;
std::cout << "operation: ";
std::cin >> temp;
op = temp.c_str();
tstream << op << "\n";
tstream.flush();
qDebug() << "dbg:" << op << "\n";

while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString response = tstream.readLine();
qDebug() << "dbg:" << response;
if(response == "SUP"){

std::cout << "operand 1: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";

std::cout << "operand 2: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";

tstream.flush();

while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString result = tstream.readLine();

std::cout << qPrintable("result is: " + result);

}else if(response == "UNS"){
std::cout << "unsupported operatoion.";
}else{
std::cout << "unknown error.";
}
emit finished();
}
  1. Что я мог сделать лучше?
  2. Каковы некоторые хорошие практики в подобных ситуациях?
  3. При использовании блокировки (не механизма «сигнал / слот»), как лучше всего обрабатывать событие, когда другая сторона закрывает соединение?
  4. Может кто-нибудь переписать это, чтобы оно выглядело более профессионально (я просто хочу посмотреть, как оно должно выглядеть, потому что я думаю, что мое решение далеко от совершенства)?
  5. Может кто-нибудь переписать это используя сигналы и слоты?

Спасибо вам.
Извините за мой английский и, наверное, глупость 🙂

4

Решение

Сетевое взаимодействие с Qt не так сложно.

Связь между двумя точками осуществляется одним классом; в случае TCP / IP это будет класс QTcpSocket. И клиент, и сервер будут взаимодействовать с объектом QTcpSocket.

Единственная разница с сервером заключается в том, что вы начинаете с объекта QTcpServer и вызываете listen () для ожидания соединения …

QTcpServer* m_pTcpServer = new QTcpServer

//create the address that the server will listen on
QHostAddress addr(QHostAddress::LocalHost); // assuming local host (127.0.0.1)

// start listening
bool bListening = m_pServer->listen(addr, _PORT); //_PORT defined as whatever port you want to use

Когда сервер получает соединение от клиента QTcpSocket, он уведомит вас newConnection сигнал, поэтому, если вы установили соединение с сокетом в своем собственном классе для получения этого сигнала, мы можем получить объект сервера QTcpSocket для связи с клиентом …

QTcpSocket* pServerSocket = m_pServer->nextPendingConnection();

Сервер будет получать объект QTcpSocket для каждого установленного соединения. Теперь сокет сервера можно использовать для отправки данных в сокет клиента, используя записывать метод …

pServerSocket->write("Hello!");

Когда сокет (клиент или сервер) получает данные, он испускает readyRead сигнал. Итак, если вы установили соединение с сигналом readyRead для сокета, функция слота может получить данные …

QString msg = pSocket->readAll();

Другой код, который вам понадобится, — обрабатывать сигналы подключения, отключения и ошибки, которые вы должны подключить к соответствующим слотам для получения этих уведомлений.

Убедитесь, что вы отправляете данные только тогда, когда знаете, что соединение установлено. Обычно я получаю соединение с сервером и отправляю клиенту сообщение «привет». Как только клиент получает сообщение, он знает, что может отправить его на сервер.

Когда одна из сторон отсоединяется, оставшаяся сторона получит сигнал отключения и может действовать соответствующим образом.

Что касается клиента, он будет иметь только один объект QTcpSocket и после вызова connectToHost, вы либо получите связано сигнал, если соединение было успешно установлено, или ошибка сигнал.

Наконец, вы можете использовать QLocalServer и QLocalSocket одинаково, если вы просто пытаетесь обмениваться данными между процессами на одном компьютере.

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]