Russian Qt Forum

Qt => Работа с сетью => Тема начата: RedDog от Сентябрь 02, 2015, 12:54



Название: QTcpSocket::readAll() SIGSEGV
Отправлено: RedDog от Сентябрь 02, 2015, 12:54
В TCP сервере сокет пускается в отдельном потоке:
Код:
void CSocketThread::run()
{
    client = QSharedPointer< QTcpSocket >( new QTcpSocket() );
    client->setSocketDescriptor( socketDescriptor );
    QObject::connect( client.data(), SIGNAL( readyRead() ),
                      this, SLOT( on_dataReceived() ), Qt::QueuedConnection );
    exec();
}

Код:
void CSocketThread::on_dataReceived()
{
    QTcpSocket* socket = qobject_cast< QTcpSocket* >( sender() );
    if( !socket )
    {
        return;
    }

    int bytesToRead = socket->bytesAvailable();

    QByteArray tmp = socket->read( bytesToRead );
......
......
......
}

Вот в самом сервере создается поток для сокета.
Код:
void CTcpServer::incomingConnection( qintptr handle )
{
    client = QSharedPointer< CSocketThread >( new CSocketThread( handle ) );
    QObject::connect( client.data(), &CSocketThread::dataReceived,
                      this, &CTcpServer::on_dataReceived );
    QObject::connect( client.data(), &QThread::finished,
                      client.data(), &QObject::deleteLater );
    client->start();
}

При чтении из сокета возникает SIGSEGV. Вылетает в случайном порядке. По объему данных никакой корреляции не увидел. Крайний раз bytesToRead = 608
ЧЯДНТ?
PS: Qt 5.3.0


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: kuzulis от Сентябрь 02, 2015, 13:30
Цитировать
ЧЯДНТ?

Переменная QTcpSocket живет в потоке CSocketThread::run(), но bytesAvailable() и read() выполняются из главного.

ЗЫ: Ну сколько же раз будут еще такие темы подниматься? Самому проверить и подумать не судьба?


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: RedDog от Сентябрь 02, 2015, 13:40
Переменная QTcpSocket живет в потоке CSocketThread::run(), но bytesAvailable() и read() выполняются из главного.
Тогда встречный вопрос: как сделать что бы они в том же потоке выполнялись?


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: torwig от Сентябрь 02, 2015, 14:04
В потоке, в котором создаете сокет, делаете все connect() и слоты там же пусть отрабатывают. Абсолютно всю работу с сокетом выносите в тот поток, где он был создан.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: RedDog от Сентябрь 02, 2015, 14:17
В потоке, в котором создаете сокет, делаете все connect() и слоты там же пусть отрабатывают. Абсолютно всю работу с сокетом выносите в тот поток, где он был создан.
Так у меня так все и сделано. В run() создаю сокет, там же делаю коннекты к слотам.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: qate от Сентябрь 04, 2015, 12:07
ЧЯДНТ?

наследуешся от qthread ?

https://wiki.qt.io/Threads_Events_QObjects


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Bepec от Сентябрь 04, 2015, 14:13
qate поясните пожалуйста.

Возможно человек просто не знал, что при наследовании от QThread необходимо в конструктор добавлять moveToThread(this), дабы слоты и остальные ф-ции принадлежали потоку, а не оставались в потоке родителя.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: qate от Сентябрь 04, 2015, 15:11
qate поясните пожалуйста.

я имел ввиду, что методика не наследоваться от qthread описано в http://doc.qt.io/qt-5/qthread.html

чем плох moveToThread(this) не могу сказать



Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Авварон от Сентябрь 04, 2015, 15:58
чем плох moveToThread(this) не могу сказать

Код:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). 
This means that all of QThread's queued slots will execute in the old thread.
Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach;
new slots should not be implemented directly into a subclassed QThread.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Авварон от Сентябрь 04, 2015, 16:00
чем плох moveToThread(this) не могу сказать

Код:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). 
This means that all of QThread's queued slots will execute in the old thread.
Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach;
new slots should not be implemented directly into a subclassed QThread.

Типа после moveToThread(this) у вас контрроллер треда (QThread) будет жить в треде, к-ый он контролирует и, по-хорошему, все его ф-ии (quit/wait) надо будет звать из этого потока. Типа если будете дергать из главного потока, то работать будет, но некошерно.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Bepec от Сентябрь 04, 2015, 17:31
Столько копий было сломано, но в результате вывод - это равнозначные методы применения :)


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: RedDog от Сентябрь 07, 2015, 09:09
И все равно не понимаю...
Объект создал в потоке, подконнектил его там же.
Так почему слоты вызываются в другом потоке то?


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Igors от Сентябрь 07, 2015, 09:50
И все равно не понимаю...
Объект создал в потоке, подконнектил его там же.
Так почему слоты вызываются в другом потоке то?
Потому что так работает Qt::AutoConnection


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: RedDog от Сентябрь 07, 2015, 10:56
Потому что так работает Qt::AutoConnection
Однако, у меня Qt::QueuedConnection


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: kuzulis от Сентябрь 07, 2015, 11:21
Цитировать
Qt::QueuedConnection

Все верно в этом случае - слот будет вызван из главного потока приложения. Можно попробовать задать DirectConnection (я не уверен что это правильный путь), но
тогда придется "защищать" прочитанные данные tmp, если планируется их передавать в основной поток:

Код
C++ (Qt)
...
int bytesToRead = socket->bytesAvailable();
QByteArray tmp = socket->read( bytesToRead );
...
 
// здесь надо что-то делать с tmp!
 
 


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Igors от Сентябрь 07, 2015, 11:25
Потому что так работает Qt::AutoConnection
Однако, у меня Qt::QueuedConnection
Ну так тем более  :) Сигнал направляется в EventLoop нитки получателя (той где он был создан или поставили череp moveToThread). А не отправителя. Жевалось хз сколько раз.


Название: Re: QTcpSocket::readAll() SIGSEGV
Отправлено: Bepec от Сентябрь 07, 2015, 14:18
В конструкторе своего отнаследованного от QThread класса вставьте строчку moveToThread(this) и будет вам счастье.