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

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

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

Сообщений: 1025


Просмотр профиля
« : Апрель 27, 2011, 17:41 »

Привет. В Qt я новичек. С многопоточностью (в Qt) только столкнулся.
Как лучше реализовать такую задачу:
Есть TCP 4 сервера. Нужно к каждому из них подключаться (независимо), делать запросы и получать данные.
Резонно что на каждое TCP соединение имеет смысл выделить по потоку.

Код:
class QMMNClientThread : public QThread
{
    Q_OBJECT

    QTcpSocket   * fmmnClient; //сетевой клиент
    QTimer        * freqTimer; //таймер, в котором надо переодически слать запрос на сервер (каждые 250мс)
    void            run();

public:

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

    QString         remoteHost;
    quint16         remotePort;

signals:

public slots:

private slots:

    void            readyRead();
    void            onConnected();
    void            onDisconnected();
    void            onError();
    void            doRequest();

};

вот моя реализация класса
Код:
#include <memory.h>
#include <QObject>
#include "mmn_client.h"
#include "qmmnclientthread.h"

QMMNClientThread::QMMNClientThread(QObject *parent) :
    QThread(parent)
{
    remoteHost = "127.0.0.1";
    remotePort = 10000;
    freqTimer = new QTimer;
    freqTimer->setInterval(250);
    fmmnClient->moveToThread(this);
    freqTimer->moveToThread(this);
    connect(fmmnClient,SIGNAL(connected()),this,SLOT(onConnected()));
    connect(fmmnClient,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
    connect(fmmnClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));
    connect(fmmnClient,SIGNAL(readyRead()),this,SLOT(readyRead()));
    connect(freqTimer,SIGNAL(timeout()),this,SLOT(doRequest()));
}

QMMNClientThread::~QMMNClientThread()
{
    freqTimer->stop(); //надо остановить таймер
    fmmnClient->close(); //закрыть соединение
    delete  fmmnClient; //удалить объекты
    delete  freqTimer;
    if(frawBuffer) free(frawBuffer); //освободить память буфера приема
}

void    QMMNClientThread::run()
{
    fmmnClient->connectToHost(remoteHost,remotePort); //первая попытка соединения
    exec(); //запускаю петлю событий потока
}

void     QMMNClientThread::readyRead()
{
 //здесь буду обрабатывать принятые данные (порядка 4Кб)
}

void     QMMNClientThread::onConnected()
{
//при подключении к серверу начинаю делать запросы данных
    freqTimer->start(250);
}


void     QMMNClientThread::onDisconnected()
{
//если сервер отключил, то
    freqTimer->stop(); //остановить запросы
    fmmnClient->close(); //закрыть сокет
    sleep(freconnTimeot); //ждем 2 секунды
    fmmnClient->connectToHost(remoteHost,remotePort); //пытаемся подключиться
}

//пока тоже самое как и при отключении
void     QMMNClientThread::onError()
{
    freqTimer->stop();
    fmmnClient->close();
    sleep(freconnTimeot);
    fmmnClient->connectToHost(remoteHost,remotePort);
}

void     QMMNClientThread::doRequest()
{
 //здесь надо отправить небольшой пакет данных по этому же сокету
}


Проблема возникает при вызове деструктора
Код:
QMMNClientThread::~QMMNClientThread()
{
    freqTimer->stop(); //надо остановить таймер
    fmmnClient->close(); //закрыть соединение <<--------здесь вылетает окно с аварийным закрытием программы
    delete  fmmnClient; //удалить объекты
    delete  freqTimer;
    if(frawBuffer) free(frawBuffer); //освободить память буфера приема
}

Я понимаю что моя тема уже избита и стара как г-но мамонта, но как мне создать сокет, что бы можно было к нему обращаться не только в run()?
Записан
mutineer
Гость
« Ответ #1 : Апрель 27, 2011, 17:47 »

Может я что-то недоглядел, но где у тебя сокет создается?
Записан
RedDog
Гость
« Ответ #2 : Апрель 28, 2011, 08:16 »

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

Сообщений: 1025


Просмотр профиля
« Ответ #3 : Апрель 28, 2011, 10:22 »

Может я что-то недоглядел, но где у тебя сокет создается?
опечатался, сокет созлаю в конструкторе (забыл вставить когда копировал из исходника).

Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #4 : Апрель 28, 2011, 10:26 »

Необходимо создать отдельный объект, в котором будет работать сокет, в этом объекте связать все сигналы и слоты с сокетом, а уже в потоке в run() создать экземпляр этого объекта.
Проблема в следующем, что каждый поток:
-должен читать сокет
-данные с этого сокета должны быть потом обработаны в другом потоке (поток синхронизации данных)
-также по этому сокету нужно отправлять асинхронные команды на сервер (команды задаются как с главного потока гуи, так и с других потоков).
В главном потоке (или в другом) по таймеру на все сокеты отправлять пакет запроса.

Раньше все писалось под QNX/POSIX threads и с этим проблем у меня не возникало, так как сокеты там потокобезопасные по умолчанию.

Записан
RedDog
Гость
« Ответ #5 : Апрель 28, 2011, 10:46 »

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

Раньше все писалось под QNX/POSIX threads и с этим проблем у меня не возникало, так как сокеты там потокобезопасные по умолчанию.
Из дочерних потоков посылается сигнал в основной поток (который занимается обработкой данных)
Если надо отправить через сокет в потоке, то этому потоку с сокетом послать сигнал, по которому сокет будет отсылать данные.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #6 : Апрель 28, 2011, 17:32 »

С сокетами вроде разобрался, теперь возникла проблема с синхронизацией.

Код моего клиента (асинхронный)
Код:
class QMMNClient : public QObject
{
    Q_OBJECT

    QTcpSocket    * fsocket;
    QTimer        * freqTimer;
    QMutex        * fmutex;

    quint16         fblockSize;
    quint16         fleftBytes;
    quint32         freconnTimeot;

public:

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

    int      id;

    QString         remoteHost;
    quint16         remotePort;

    void            connectToServer();
    void            disconnectFromServer();
    void            moveToThread(QThread *thread);
    bool            safeGetData(mmn_frame_t * destFrame, size_t  frameSize);

signals:

    void            connected(int id);
    void            disconnected(int id);
    void            frameReceived(int id);

private slots:

    void            readyRead();
    void            onConnected();
    void            onDisconnected();
    void            doRequest();

public slots:

    void            sendRequest();

};

//реализация

QMMNClient::QMMNClient(QObject *parent) :
    QObject(parent)
{
    id = 0;
    fsocket = new QTcpSocket;
    remoteHost = "127.0.0.1";
    remotePort = 10000;
    freconnTimeot = 2;
    freqTimer = new QTimer;
    freqTimer->setInterval(250);
    fmutex = new QMutex;
//эти соыбтия обрабатываю внутри потока сетевого клиента
    connect(fsocket,SIGNAL(connected()),this,SLOT(onConnected()));
    connect(fsocket,SIGNAL(disconnected()),this,SLOT(onDisconnected()));
    connect(fsocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onDisconnected()));
    connect(fsocket,SIGNAL(readyRead()),this,SLOT(readyRead())); //здесь читаю
    connect(freqTimer,SIGNAL(timeout()),this,SLOT(doRequest())); //переодический запрос на сервер
}

QMMNClient::~QMMNClient()
{
    freqTimer->stop();
    fsocket->abort();
    delete  fsocket;
    delete  freqTimer;
    delete  fmutex;
    if(frawBuffer) free(frawBuffer);
}

void     QMMNClient::readyRead()
{
    if(fblockSize==0)
    {
        if(fsocket->bytesAvailable()<sizeof(scb_msg_t)) return;
        fblockSize = fsocket->read(frawBuffer,sizeof(scb_msg_t));
        if(fframeBuffer.msg_head->size==0)
        {
            fblockSize = 0;
            return;
        }
        qDebug("block %d\n, header.size %d",fblockSize,fframeBuffer.msg_head->size);
    }

    fblockSize=fsocket->bytesAvailable();
    fleftBytes = fframeBuffer.msg_head->size-sizeof(scb_msg_t);

    if(fblockSize>=(fleftBytes))
    {
        fmutex->lock();
        fblockSize = fsocket->read(&frawBuffer[sizeof(scb_msg_t)],fleftBytes);
        fmutex->unlock();
        qDebug("fblockSize %d\n",fblockSize);
        fblockSize = 0;
        emit frameReceived(id); //надо это событие обработать в потоке GUI
    }
}

bool     QMMNClient::safeGetData(mmn_frame_t * destFrame, size_t  frameSize)
{
    if(destFrame && (frameSize==fframeSize))
    {
        fmutex->lock();
        qMemCopy(destFrame,&fframeBuffer,fframeSize);
        fmutex->unlock();
        return true;
    }
    return false;
}

void     QMMNClient::onConnected()
{
    fblockSize = 0;
//    qDebug("connected channel %d\n",id);
    emit    connected(id);
    freqTimer->start(250);
}

void     QMMNClient::onDisconnected()
{
//    qDebug("disconnected channel %d\n",id);
    freqTimer->stop();
    fsocket->close();
    emit    disconnected(id);
    sleep(freconnTimeot);
    fsocket->connectToHost(remoteHost,remotePort);
}

void     QMMNClient::doRequest()
{
    sendRequest();
}

void     QMMNClient::connectToServer()
{
    fsocket->connectToHost(remoteHost,remotePort);
}

void     QMMNClient::disconnectFromServer()
{
    fsocket->close();
}

void      QMMNClient::sendRequest()
{
    fsocket->write((const char*)&frequest,frequestSize); //отправляем некую структурку на сервер, на которую он должен ответить
}

void      QMMNClient::moveToThread(QThread *thread)
{
    QObject::moveToThread(thread); //двигаем себя в нужный поток
    if(fsocket) fsocket->moveToThread(thread); //сокет
    if(freqTimer) freqTimer->moveToThread(thread); //и таймер
}

Код потока, в котором будет крутиться сокет
Код:
class QMMNClientThread : public QThread
{
    Q_OBJECT

    void            run();

public:
    explicit QMMNClientThread(QObject *parent = 0);
    ~QMMNClientThread();

    QMMNClient   *   mmnClient;

signals:

public slots:

};

// реализация

QMMNClientThread::QMMNClientThread(QObject *parent) :
    QThread(parent)
{
    mmnClient = new QMMNClient;
    mmnClient->moveToThread(this);
}

QMMNClientThread::~QMMNClientThread()
{
    quit();
    wait();
}

void    QMMNClientThread::run()
{
    mmnClient->connectToServer();
    exec();
}

В объекте главного потока создаю потоки TCP клиентов, связываю сигналы, которые будут генериться при событиях в потоках клиентов, и должны обрабатываться в главном потоке.

Код:
        mmnClientThread = new QMMNClientThread(this);
        mmnClientThread->mmnClient->remoteHost = ecHost[i];
        mmnClientThread->mmnClient->remotePort = ecPort[i];
        connect(mmnClientThread->mmnClient,SIGNAL(frameReceived(int)),this,SLOT(data_receive(int)),Qt::BlockingQueuedConnection);//здесь будет обращение к GUI
        connect(mmnClientThread->mmnClient,SIGNAL(connected(int)),this,SLOT(chan_connected(int)));
        connect(mmnClientThread->mmnClient,SIGNAL(disconnected(int)),this,SLOT(chan_disconnected(int)));
        mmnClientThread->start();

так вот при emit frameReceived(id) я захожу в обработчик data_receive(id), но указатель на класс в отладчике не показывается, и вообще все данные объекта как бы испорчены, и вылеатет SIGSEGV.
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #7 : Апрель 28, 2011, 18:01 »

В отладчике на this ругается : <unavailable synchroneous data>
ощущение что стек покоцался...

также связал сигнал дочернего сетевого потока и основного ГУИ с поправкой
connect(mmnClientThread->mmnClient,SIGNAL(frameReceived(int)),this,SLOT(data_receive(int)),Qt::DirectConnection);

все равно SIGSEGV.


нашёл ошибку, банально копировал не в тот указатель, что надо...
пока все работает.
« Последнее редактирование: Апрель 28, 2011, 18:28 от Fregloin » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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