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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Помогите с потоком пожалуйста  (Прочитано 5882 раз)
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« : Сентябрь 16, 2013, 15:38 »

Набросал простейший клиент, в котором tcp сокет живет в наслелнике QThread.
Вылетает через несколько секунд после начала работы на readAll()

Код:
#ifndef CMYSOCKET_H
#define CMYSOCKET_H

#include <QThread>
#include <QTcpSocket>
#include <QTimer>

class CMySocket : public QThread
{
    Q_OBJECT

    QTcpSocket  *   fsocket;
    QTimer      *   frequestTimer;

    QString         fhost;
    quint16         fport;

    size_t          freceived;
    size_t          fguardSize;
    QByteArray      fcurrentData;

protected:

    void    run();

public:

    explicit CMySocket(QObject *parent = 0);
    ~CMySocket();

    inline  void    setHost(const QString & value)  {   fhost = value;  }
    inline  void    setPort(quint16 value)  {   fport = value;  }
    inline  const   QString &   host()  const   {   return  fhost;  }
    inline          quint16     port()  const   {   return  fport;  }

signals:

    void    dataReceived(QString jsonString);
    void    guardSizeExceed();
   
public slots:

private slots:

    void    socket_connect();
    void    socket_disconnect();
    void    socket_error(QAbstractSocket::SocketError);
    void    socket_read();
    void    socket_cleanup();
    void    socket_send_request();
};

#endif // CMYSOCKET_H

Код:
#include "cmysocket.h"

CMySocket::CMySocket(QObject *parent) :
    QThread(parent)
{
    fsocket = NULL;
    frequestTimer = NULL;
    fhost = "127.0.0.1";
    fport = 50001;
    fguardSize = 10485760L;
    freceived = 0L;
    connect(this,SIGNAL(finished()),this,SLOT(socket_cleanup()));
}

CMySocket::~CMySocket()
{
    socket_cleanup();
}

void CMySocket::socket_connect()
{
    qDebug("connected %s:%d",qPrintable(fhost),fport);
    //frequestTimer->start(500);
}

void CMySocket::socket_disconnect()
{
    //frequestTimer->stop();
    qDebug("disconnected %s:%d",qPrintable(fhost),fport);
}

void CMySocket::socket_error(QAbstractSocket::SocketError)
{
    qDebug("error %s:%d %s",qPrintable(fhost),fport,qPrintable(fsocket->errorString()));
}

void CMySocket::socket_read()
{
    int bytesAvail = fsocket->bytesAvailable();
    if(bytesAvail<=0) return;
    QByteArray  packetData = fsocket->readAll();    //считываем текущую часть принятых данных

    qDebug("read %d bytes",packetData.size());

    /*
    if(!packetData.isEmpty())                       //если что то есть(а полюбому что то должно быть)
    {
        int terminatorIndex;                        //индекс терминатора (нулевого символа)
        int prevTerminatorIndex = 0;                //предыдущий терминатор
        int dataSize = packetData.size();           //размер полученных данных

        terminatorIndex = packetData.indexOf('\0',prevTerminatorIndex); //ищем первый терминатор в пакете
        if(terminatorIndex<0)                       //если нет терминатора, копируем целиком
        {
            fcurrentData.append(packetData);        //добавляем полученные данные во временную строку
            freceived+=dataSize;                    //инкрементируем счетчик полученных данных
            if(freceived>=fguardSize)               //если полученных данных больше чем охранный размер
            {
                freceived = 0L;                      //обнуляем счетчик принятых данных
                qDebug("freceived %lu",freceived);
                emit guardSizeExceed();             //сигналим об ошибке
            }
            return;
        }

        int last;

        while(terminatorIndex>=0 && prevTerminatorIndex<dataSize)
        {
            last = terminatorIndex-prevTerminatorIndex;
            if(last>0)
            {
                fcurrentData.append(packetData.mid(prevTerminatorIndex,last)); //копируем часть до терминатора
                emit dataReceived(QString(fcurrentData));  //сигналим об очередной строке с json
            }
            freceived = 0L;
            qDebug("freceived %lu",freceived);
            fcurrentData.clear();                           //стираем временный буфер
            prevTerminatorIndex = terminatorIndex+1;        //сдвигаем указатель, с которого будем искать следующий терминатор
            terminatorIndex = packetData.indexOf('\0',prevTerminatorIndex);
        }

        if(prevTerminatorIndex<dataSize)    //если осталась часть принятых данных со следующей json строки
        {
            int lastPart = dataSize-prevTerminatorIndex;
            fcurrentData.append(packetData.mid(prevTerminatorIndex,(lastPart)));//копируем остаток
            freceived+=lastPart;
            qDebug("freceived %lu",freceived);
        }
    }
    */
}

void CMySocket::socket_cleanup()
{
    if(frequestTimer)
    {
        frequestTimer->stop();
        frequestTimer->deleteLater();
        frequestTimer = NULL;
    }
    if(fsocket)
    {
        fsocket->disconnectFromHost();
        fsocket->deleteLater();
        fsocket = NULL;
    }
}

void CMySocket::socket_send_request()
{
}

void CMySocket::run()
{
    if(fhost.isEmpty() || !fport)
        exit(-1);

    //frequestTimer = new QTimer;
    //connect(frequestTimer,SIGNAL(timeout()),this,SLOT(socket_send_request()));
    fsocket = new QTcpSocket;
    connect(fsocket,SIGNAL(connected()),this,SLOT(socket_connect()));
    connect(fsocket,SIGNAL(disconnected()),this,SLOT(socket_disconnect()));
    connect(fsocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(socket_error(QAbstractSocket::SocketError)));
    connect(fsocket,SIGNAL(readyRead()),this,SLOT(socket_read()));
    fsocket->connectToHost(fhost,fport);
    exec();
}

основное окно
Код:
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QMenu   *   menu = new QMenu(trUtf8("Связь"));
    QAction *   action = menu->addAction(trUtf8("Подключиться"));
    connect(action,SIGNAL(triggered()),this,SLOT(connect_sockets()));
    action = menu->addAction(trUtf8("Отключиться"));
    connect(action,SIGNAL(triggered()),this,SLOT(disconnect_sockets()));

    menuBar()->addMenu(menu);


    fsocket = new CMySocket(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::connect_sockets()
{
    fsocket->setHost("192.168.1.101");
    fsocket->setPort(50002);
    connect(fsocket,SIGNAL(dataReceived(QString)),this,SLOT(process_data(QString)));
    if(!fsocket->isRunning())
    {
        fsocket->start();
    }
}

void MainWindow::disconnect_sockets()
{
    if(fsocket->isRunning())
    {
        fsocket->quit();
        fsocket->wait();
    }
}

void MainWindow::process_data(const QString &value)
{
    //qDebug("data %s",qPrintable(value));
}

По сути это вырожденный сетевой клиент, который работал раньше (но нужно довести до ума).
Почему у меня вылетает при чтении с сокета. В консоли ругани на принадлежность к разным объектам нет.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #1 : Сентябрь 16, 2013, 15:51 »

Добавил вывод инфы, кто где запускается, и вот что имеем:
Код:
CMySocket::CMySocket(QObject *parent) :
    QThread(parent)
{
    fsocket = NULL;
    frequestTimer = NULL;
    fhost = "127.0.0.1";
    fport = 50001;
    fguardSize = 10485760L;
    freceived = 0L;
    connect(this,SIGNAL(finished()),this,SLOT(socket_cleanup()));
    qDebug("CMySocket lives in %p, currentThread %p",this->thread(),currentThread());
}

CMySocket::~CMySocket()
{
    socket_cleanup();
}

void CMySocket::socket_connect()
{
    qDebug("connected %s:%d",qPrintable(fhost),fport);
    frequestTimer->start(500);
}

void CMySocket::socket_disconnect()
{
    frequestTimer->stop();
    qDebug("disconnected %s:%d",qPrintable(fhost),fport);
}

void CMySocket::socket_error(QAbstractSocket::SocketError)
{
    qDebug("error %s:%d %s",qPrintable(fhost),fport,qPrintable(fsocket->errorString()));
}

void CMySocket::socket_read()
{
    qDebug("CMySocket::socket_read() %p",currentThread());
    int bytesAvail = fsocket->bytesAvailable();
    if(bytesAvail<=0) return;
    QByteArray  packetData = fsocket->readAll();    //считываем текущую часть принятых данных
    qDebug("read %d bytes",packetData.size());
}

void CMySocket::socket_cleanup()
{
    if(frequestTimer)
    {
        frequestTimer->stop();
        frequestTimer->deleteLater();
        frequestTimer = NULL;
    }
    if(fsocket)
    {
        fsocket->disconnectFromHost();
        fsocket->deleteLater();
        fsocket = NULL;
    }
}

void CMySocket::socket_send_request()
{
}

void CMySocket::run()
{
    if(fhost.isEmpty() || !fport)
        exit(-1);

    frequestTimer = new QTimer;
    connect(frequestTimer,SIGNAL(timeout()),this,SLOT(socket_send_request()));
    fsocket = new QTcpSocket;
    connect(fsocket,SIGNAL(connected()),this,SLOT(socket_connect()));
    connect(fsocket,SIGNAL(disconnected()),this,SLOT(socket_disconnect()));
    connect(fsocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(socket_error(QAbstractSocket::SocketError)));
    connect(fsocket,SIGNAL(readyRead()),this,SLOT(socket_read()));
    fsocket->connectToHost(fhost,fport);
    qDebug("CMySocket::run() current thread %p, socket in %p",this->currentThread(),fsocket->thread());
    exec();
}

вывод отладчика

CMySocket lives in 0x2083f10, currentThread 0x2083f10
CMySocket::run() current thread 0x23bf130, socket in 0x23bf130
connected 192.168.1.101:50002
CMySocket::socket_read() 0x2083f10
read 373 bytes
CMySocket::socket_read() 0x2083f10
read 373 bytes
CMySocket::socket_read() 0x2083f10
read 373 bytes


получается, что сокет создается в отдельном потоке, но слоты вызываются в потоке, в котором создан CMySocket. А как мне сделать, что бы слоты вызывались в том, потоке, в котром живет сокет??
Записан
mutineer
Гость
« Ответ #2 : Сентябрь 16, 2013, 15:54 »

Не наследоваться от QThread, а создать отдельный объект со слотами и всем прочим. Создать QThread, запустить его, а затем мувнуть объект в тред
Записан
Bepec
Гость
« Ответ #3 : Сентябрь 16, 2013, 15:55 »

Код:
moveToThread(this);
в конструкторе потока спасёт вас.

PS и если можно, дублируйте код в архиве - на форуме довольно неудобно его просматривать.

update: Ответы mutineer и меня - два различных способа решения этой проблемы.

PS равнозначных способа ^.^
Записан
mutineer
Гость
« Ответ #4 : Сентябрь 16, 2013, 16:04 »

PS равнозначных способа ^.^

ну да, вопрос религии:)
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #5 : Сентябрь 16, 2013, 16:05 »

Не наследоваться от QThread, а создать отдельный объект со слотами и всем прочим. Создать QThread, запустить его, а затем мувнуть объект в тред
Так тоже делал, но при завершении потока через quit() он звисал, wait() выдавал ошибку завершения потока...
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #6 : Сентябрь 16, 2013, 19:04 »

Сделал по другому:

Код:
#include <QThread>
#include "qdspclient.h"

class QDspClientThread : public QThread
{
    Q_OBJECT

public:
    explicit QDspClientThread(QObject *parent = 0);
    ~QDspClientThread();
    QDspClient  *   dspClient;

};

Код:
QDspClientThread::QDspClientThread(QObject *parent) :
    QThread(parent)
{
    dspClient = new QDspClient;
    connect(this,SIGNAL(started()),dspClient,SLOT(connectToServer()));
    connect(this,SIGNAL(finished()),dspClient,SLOT(disconnectFromServer()));
}

QDspClientThread::~QDspClientThread()
{
    dspClient->deleteLater();
}

Вот как использую
Код:
void    CDspHandler::startClient()
{
    foreach(QDspClientThread * clientThread, fdspConnections)
    {
        if(!clientThread->isRunning())
        {
            if(clientThread->dspClient->connectionConfig().interfaceIndex()>=0)
            {
                clientThread->start();
                clientThread->dspClient->moveToThread(clientThread->currentThread());
            }
        }
    }

    emit    sourceChanged(NULL);
}

void    CDspHandler::stopClient()
{
    foreach (QDspClientThread   *   clientThread, fdspConnections)
    {
        if(clientThread->isRunning())
        {
            qDebug("quiting thread for %s:%d",qPrintable(clientThread->dspClient->connectionConfig().host()),clientThread->dspClient->connectionConfig().port());
            clientThread->quit();
            bool result = clientThread->wait(5000);
            qDebug(result?"Ok":"Failed");
            clientThread->dspClient->moveToThread(thread());
        }
    }
}

На потоки не ругается, но зависает на
Код:
void     QRawClient::connectToServer()
{
    fmustReconnect = true;
    if(fsocket.state() != QAbstractSocket::UnconnectedState) return;

    int fd = fsocket.socketDescriptor();
    if(fd<0)
    {
        fd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
    }

    if(!fbindAddress.isNull())   //если биндинг задан, создаем сокет посредством Qt
    {
//      qDebug("Try bind %i:%s to %s",connectionConfig().interfaceIndex(),qPrintable(connectionConfig().host()),qPrintable(fbindAddress.toString()));

        struct sockaddr_in bindAddr;

        memset(&bindAddr,0,sizeof(bindAddr));
        bindAddr.sin_family = PF_INET;
        bindAddr.sin_addr.s_addr = htonl(fbindAddress.toIPv4Address());

        if(bind(fd,(const struct sockaddr *)&bindAddr,sizeof(bindAddr))!=0)
        {
            qDebug("Socket bind to %s failed with reason %s",qPrintable(fbindAddress.toString()),strerror(errno));
            shutdown(fd,2);
#ifdef Q_WS_WIN32
            closesocket(fd);
#else
                close(fd);
#endif
            onDisconnected();
            return;
        }
    }

    struct sockaddr_in connect_addr;
    memset(&connect_addr,0,sizeof(connect_addr));
    connect_addr.sin_family = PF_INET;
    connect_addr.sin_port = htons(connectionConfig().port());

    QHostAddress address(connectionConfig().host());
    connect_addr.sin_addr.s_addr = htonl(address.toIPv4Address());

    if(::connect(fd,(const struct sockaddr *)&connect_addr,sizeof(connect_addr))==-1) //<<<<<ЗДЕСЬ ЗАВИСАЕТ
    {
        qDebug("connect error! %s",strerror(errno));
        close(fd);
        onDisconnected();
        return;
    }
    else
    {
        if(!fsocket.setSocketDescriptor(fd,QAbstractSocket::ConnectedState))
           qDebug("binding to native socket failed");
        onConnected();
    }
}
Записан
Bepec
Гость
« Ответ #7 : Сентябрь 17, 2013, 08:02 »

Код:
moveToThread(this);
в конструкторе потока спасёт вас.

PS и если можно, дублируйте код в архиве - на форуме довольно неудобно его просматривать.

update: Ответы mutineer и меня - два различных способа решения этой проблемы.

PS равнозначных способа ^.^

PS интересно, научатся ли люди читать все комментарии, а не через один? Улыбающийся
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #8 : Сентябрь 17, 2013, 09:04 »

думаете я не делал, как было указано выше? Если бы заработало, я бы тут уже не спрашивал. Но увы, в упор виснет на connect(scoket).
Записан
Bepec
Гость
« Ответ #9 : Сентябрь 17, 2013, 09:32 »

Но никакого коммента вы не выделили мне Улыбающийся И в коде не видно ни одной такой строки Улыбающийся

Выкидывайте проект в архиве, если осмелитесь - разберёмся.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #10 : Сентябрь 17, 2013, 15:50 »

вырезаю все что связано с моими сокетами (надеюсь на ваши советы по поводу их улучшения)
« Последнее редактирование: Сентябрь 17, 2013, 16:17 от Fregloin » Записан
Bepec
Гость
« Ответ #11 : Сентябрь 17, 2013, 17:14 »

Ну зачем вы, создатели тем, так меня мучаете?

Или это уже привычка - просят проект - кинем пару обрезанных файлов. Просят архив - кинем половину. Хотим полный скриншот - на те вам скриншот активного окна Веселый

PS Улыбающийся
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #12 : Сентябрь 18, 2013, 10:37 »

ответ банален, это коммерческий проект, который я не могу просто вязть и выложить на обозрение всем.
мне нужна помощь. я понимаю что из обрезков целая картина врядли получится, но исходники выкладыватьс здесь я категорически не могу. думаю Вы бы тоже так не делали.
Записан
Bepec
Гость
« Ответ #13 : Сентябрь 18, 2013, 11:40 »

Вы всегда можете взять, сесть и в пустой проект добавить ваши классы и сделать минимально работоспособный проект. Он будет делать тупо проверку. И это для вас - дело пяти минут. Для других - нужно понять взаимосвязи и назначение ваших классов, как то их присобачить друг с другом, заставить скомпилироваться и задуматься - а что же должно быть в результате?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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