LINUX.ORG.RU

Почему сокет ведет себя странно? (Qt)

 ,


0

2

Имею код на стороне клиента:

void CAptekaClient::send(quint8 code, QString text)
{

    m_socket->waitForConnected();
    if (m_waiting) {
        m_socket->waitForReadyRead();
    }

    QString textCode;
    textCode.setNum(code, 10);
    qDebug() << "Sending..." << endl << "CODE: " << textCode << " TEXT: " << text;

    QDataStream data(m_socket);

    quint32 bytesSend = 0;
    QByteArray message = text.toUtf8();

    bytesSend += (int)sizeof(quint32);
    bytesSend += (int)sizeof(quint8);
    bytesSend += message.size();

    data << bytesSend << code;     //************* ТУТ

    if (text.length() > 0) {
        QTextStream textData(data.device());
        textData << message;
    }

    m_socket->flush();             //************* ТУТ

Делаю очередь «посылок» команд серверу. А точнее, три: привет, авторизируй, прочитай данные. В момент отсыла третьей команды происходит следующее:

QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread

Первая ошибка возникает на строке с первым комментарием «тут», вторая - на второй.

Помогите разобраться, пожалуйста, почему они возникают? По потокам не прыгаю в момент отсылки. Из других потоков в этот момент ЕМНИП ничего не происходит (все ждут). Так в чем проблема?

★★★★★

Ответ на: комментарий от panter_dsd

хм...

class CClient : public QObject {
Q_OBJECT
   CClient(QObject *parent = 0);
   void init();
   void start();
public slots:
   void slot_onStart();
   void slot_onConnected();
private:
   QTcpSocket *m_socket;
   QThread *m_thread;
   QString m_addr;
   int m_port;
};

CClient::CClient()
{
    //m_socket = new QTcpSocket(this);
    m_thread = new QThread(this);
    m_blockSize = 0;

    //connect(m_socket, SIGNAL(readyRead()), this, SLOT(slot_onReadyRead()));

}

void CClient::init(QString host, int port)
{
    m_addr.setAddress(host);
    m_port = port;

    this->start();
}

void CClient::start()
{
    if (this->m_thread->isRunning()) {
        m_thread->quit();
    }
    this->moveToThread(m_thread);
    connect(m_thread, SIGNAL(started()), this, SLOT(slot_onStart())); // при старте работы потока запустить слот
    connect(this, SIGNAL(signal_finished()), m_thread, SLOT(quit())); // при окончании работы этого класса завершить поток

    m_thread->start();
}

void CClient::slot_onStart()
{
    m_socket = new QTcpSocket(this);
    connect(m_socket, SIGNAL(readyRead()), this, SLOT(slot_onReadyRead()));
    connect(m_socket, SIGNAL(connected()), this, SLOT(slot_onConnected()));
    m_socket->connectToHost(m_addr, m_port);
}

void CClient::slot_onConnected()
{
    m_isRunning = true;
    send(ProtocolCommands::Hello);
}




//ВЫЗОВ
CClient *client = new CClient();
client->init("127.0.0.1", 3333);
client->send("testing test");

Код не 100%-ный. Выкинул все вспомогательное, чтобы смысл остался.

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от bvn13

Если в коде есть

this->moveToThread(m_thread);
то либо ты не понимаешь как работают qt сокеты, либо у тебя плохая архитектура.

alpha4
()
Ответ на: комментарий от bvn13

А так пробовали:

    connect(m_thread, SIGNAL(started()), this, SLOT(slot_onStart())); // при старте работы потока запустить слот
    connect(this, SIGNAL(signal_finished()), m_thread, SLOT(quit())); // при окончании работы этого класса завершить поток 
    this->moveToThread(m_thread); // Теперь переместить в другой поток
no-such-file ★★★★★
()
Ответ на: комментарий от bvn13

А вообще, конечно, this->moveToThread внушает некоторые сомнения

no-such-file ★★★★★
()
Ответ на: комментарий от trex6

хм. Читал не так давно статью Bradley T. Hughes. Сейчас по Вашему «посылу» нашел статью-ответ бывшего коллеги Бредли. Я так понимаю, мне вообще дополнительный поток ни к чему, правильно? Но как-то (не наследуясь от QTcpSocket) у меня не возникает параллельности. Возможно, I did it wrong, конечно... :) Что можете посоветовать в моем клиническом случае?

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

только что попробовал. Результат тот же.

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от bvn13

В дополнение к you're doing it wrong... еще можно (нужно?) почитать это: http://blog.qt.digia.com/blog/2006/12/04/threading-without-the-headache/

Там есть вполне понятный пример. Единственное что, начиная с какой-то версии Qt наследовать QThread и добавлять в run вызов exec () уже не надо - он и так там...

solovey ★★
()
Ответ на: комментарий от trex6

you do it wrong QThread

мне таки интересно - откуда пошло это замечательное this->moveToThread(thread), даже на ЛОРе уже хз сколько раз было

Stil ★★★★★
()
Ответ на: комментарий от Stil

При поиске информации о QThread в большинстве статей упоминается именно этот вариант. А сама документация о QThread не позволяет получить полной картины.

По моему мнению статью you_do_it_wrond надо запилить внутрь документации по QThread.

trex6 ★★★★★
()
Последнее исправление: trex6 (всего исправлений: 1)
Ответ на: комментарий от Stil

this->moveToThread(thread)

Вообще-то в оригинале было (this->)moveToThread(this), где this это наследник QThread, здесь же this это какой-то левый объект клиента, то есть получается client->moveToThread(thread), что является вполне «правильным» решением.

anonymous
()
Ответ на: комментарий от bvn13

Скорее всего проблема в том, что ты переместил свой клиент в другую нить, а потом в основной нити делаешь:

client->send(«testing test»);

Поэтому вот этот твой код

data << bytesSend << code; //************* ТУТ

вызывается в основной нити, хотя data вместе с клиентом и сокетом уже находятся в другом.

anonymous
()
Ответ на: комментарий от anonymous

И исправить это можно, сделав send сигналом в одном месте и связать его с каким-нибудь слотом в клиенте.
Либо сделать Client::send слотом и вызывать через QMetaObject::invokeMethod (костыли).

anonymous
()
Ответ на: комментарий от bvn13

moveToThread

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

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

И что ты предлагаешь с этим делать?

иногда слоты просто виснут, иногда не вызываются

Ссылки на багрепорты в студию.

anonymous
()
Ответ на: комментарий от anonymous

Ссылки на багрепорты в студию.

какие тебе еще ссылки, в документации написано как делать низя, а если так будет - то и будет виснуть и это не баг, а непродуманное использование

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

написано как делать низя, а если так будет - то и будет

Наркоман какой-то писал, неудивительно, что у тебя слоты виснут и не вызываются.

anonymous
()
Ответ на: комментарий от anonymous

Как бы, да. У меня не тот вариант, что в статье.

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от bvn13

Перед тем как задаваться этим вопросом, проверил бы мое предположение, благо это можно сделать быстро.
Если окажется что не из-за этого, то смысл вопроса теряется.
Если из-за этого, то бери в руки дебаггер или принтф'ы (qDebug'и), что тебе больше нравится, и наблюдай за процессом.

anonymous
()
Ответ на: комментарий от pulo

Офигеть совет. Если уж он не может в Delp^WQt разобраться, то какие у него шансы с posix сокетами?

anonymous
()
Ответ на: комментарий от trex6

Просветите, пожалуйста.

Если я сделаю аналогию примеру threaded fortune client (из примеров), т.е. класс, работающий непосредственно с сокетом (создание и чтение/запись из/в сокет), я унаследую от QThread (и буду правильно его создавать, соединять сигнал disconnected с его же слотом deleteLayer), а в методе run() планирую организовать всю работу с сокетом, то будет ли правильным, если я создам сокет непосредственно в этом run()-методе, а чтение и запись вынесу в другие методы этого класса, будет ли это означать, что вязывая эти методы из run(), я буду их выполнять в том же thread?

наверное, сумбурно, пример:

class CClientThread : QThread {
public:
   void run();
   void writeToSocket(QTcpSocket *socket, QString data);
   QString readFromSocket(QTcpSocket *socket);
};

void CCLientThread::run()
{
   QTcpSocket socket;
   writeToSocket(&socket);  // или, может быть, лишь десткриптор передавать?
   QString res = readFromSocket(&socket);
}

или я совсем запутался и полез «невтустэп»?

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от pulo

хочется разобраться именно с QTcpSocket-ами

bvn13 ★★★★★
() автор топика
Ответ на: комментарий от bvn13

попробовал - прокатило.

всем спасибо за помощь и наводки на истину!

bvn13 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.