Russian Qt Forum
Октябрь 04, 2024, 22:27 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Threadedfortuneserver - добавить чтение клиентского сообщения.  (Прочитано 3693 раз)
Last
Гость
« : Март 21, 2013, 13:22 »

Всем привет, суть проблемы в названии (threadedfortuneserver - пример из дистрибутива Qt). В моей текущей реализации ответ клиенту приходит, но сервер сразу после этого падает. Отладчиком найти место падения не получается, т.к. оно происходит где-то в дизассемблированных кодах, в коих я ничего не понимаю.
Мой вариант thread.h:
Код:
class Thread : public QThread
{
  Q_OBJECT

public:
  Thread(int socketDescriptor, const QString &fortune, QObject *parent);
  void run();
 
signals:
  void error(QTcpSocket::SocketError socketError);
 
private:
  int socketDescriptor;
  QTcpSocket *tcpSocket;
  QString text;
  quint16 blockSize;

private slots:
  void readClient();
};
Кусок server.cpp:
Код:
void Server::incomingConnection(qintptr socketDescriptor)
{
  QString fortune = fortunes.at(qrand() % fortunes.size());
  Thread *thread = new Thread(socketDescriptor, fortune, this);
  connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
  thread->start();
}
И гвоздь программы - thread.cpp:
Код:
void Thread::run()
{
  tcpSocket = new QTcpSocket(NULL);
  if (!tcpSocket->setSocketDescriptor(socketDescriptor)) {
    emit error(tcpSocket->error());
    return;
  }
  blockSize = 0;
  connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readClient()));
  connect(tcpSocket, SIGNAL(disconnected()), tcpSocket, SLOT(deleteLater()));
  connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(quit()));
  tcpSocket->waitForDisconnected();
}

void Thread::readClient()
{
  QDataStream in(tcpSocket);
  in.setVersion(QDataStream::Qt_4_0);
  if (blockSize == 0) {
    if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
      return;
    in >> blockSize;
  }

  if (tcpSocket->bytesAvailable() < blockSize)
    return;
  in >> text;
  QByteArray block;
  QDataStream out(&block, QIODevice::WriteOnly);
  out.setVersion(QDataStream::Qt_4_0);
  out << (quint16)0;
  out << text;
  out.device()->seek(0);
  out << (quint16)(block.size() - sizeof(quint16));
  tcpSocket->write(block);
  tcpSocket->disconnectFromHost();
}

Ошибки из Application Output
Цитировать
QSocketNotifier: socket notifiers cannot be enabled from another thread
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x12ba6e40), parent's thread is Thread(0x12ba80e0), current thread is QThread(0x12b3e0b0)
QSocketNotifier: socket notifiers cannot be disabled from another thread
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 12b3e0b0. Receiver '' (of type 'QNativeSocketEngine') was created in thread 12ba80e0", file kernel\qcoreapplication.cpp, line 420
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
QNativeSocketEngine::waitForWrite() was called on an uninitialized socket device
Я понимаю, что эти ошибки связаны с наличием tcpSocket'a в классе треда. У меня своего рода дилемма: если убрать tcpSocket из класса треда, то сигнал readyRead() вызовется один раз, после чего тред просто повиснет и клиент не получит ответа, а если tcpSocket не убирать, приложение вообще падает с ASSERT FAILURE.
Подскажите пожалуйста, если есть идеи, как можно выбраться из такой ситуации.
Записан
k0p4
Гость
« Ответ #1 : Март 21, 2013, 15:31 »

Цитировать
Thread *thread = new Thread(socketDescriptor, fortune, this);
Трэд был создан и висит в главном потоке. Соккет - член класса трэд. Значит тоже висит в главном. Метод  run() выполняется в параллельном потоке. Отсуда и проблемы. Сделай moveToThread для соккета.

Вообще в методе run() должен быть только exec();. Т.е, запускаем локальный ивентлуп трэда, для получения ивентов. При этом incomingConnection будет выглядеть примерно так:
Код:
void Server::incomingConnection(qintptr socketDescriptor)
{
  ....
  Socket *socket = new Socket;
  Thread *thread = new Thread();
  thread->start();
  socket->moveToThread(thread);
  connect(socket, SIGNAL(disconnected()), thread, SLOT(deleteLater()));
  connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));  
}

А всю обработку вместо трэда вынести в класс соккета.
« Последнее редактирование: Март 21, 2013, 15:41 от k0p4 » Записан
Last
Гость
« Ответ #2 : Март 24, 2013, 23:00 »

Спасибо, уже раскурил маны с подачи людей, сидевших в IRC.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.117 секунд. Запросов: 20.