| 
 Название: Несколько TCP клиентов в одной программе
 Отправлено: Fregloin от Апреля 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()?
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: mutineer от Апреля 27, 2011,  17:47
 
 Может я что-то недоглядел, но где у тебя сокет создается? 
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: RedDog от Апреля 28, 2011,  08:16
 
 Необходимо создать отдельный объект, в котором будет работать сокет, в этом объекте связать все сигналы и слоты с сокетом, а уже в потоке в run() создать экземпляр этого объекта. 
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: Fregloin от Апреля 28, 2011,  10:22
 
 Может я что-то недоглядел, но где у тебя сокет создается?
 опечатался, сокет созлаю в конструкторе (забыл вставить когда копировал из исходника).
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: Fregloin от Апреля 28, 2011,  10:26
 
 Необходимо создать отдельный объект, в котором будет работать сокет, в этом объекте связать все сигналы и слоты с сокетом, а уже в потоке в run() создать экземпляр этого объекта.
 Проблема в следующем, что каждый поток: -должен читать сокет -данные с этого сокета должны быть потом обработаны в другом потоке (поток синхронизации данных) -также по этому сокету нужно отправлять асинхронные команды на сервер (команды задаются как с главного потока гуи, так и с других потоков). В главном потоке (или в другом) по таймеру на все сокеты отправлять пакет запроса. Раньше все писалось под QNX/POSIX threads и с этим проблем у меня не возникало, так как сокеты там потокобезопасные по умолчанию.
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: RedDog от Апреля 28, 2011,  10:46
 
 Проблема в следующем, что каждый поток:-должен читать сокет
 -данные с этого сокета должны быть потом обработаны в другом потоке (поток синхронизации данных)
 -также по этому сокету нужно отправлять асинхронные команды на сервер (команды задаются как с главного потока гуи, так и с других потоков).
 В главном потоке (или в другом) по таймеру на все сокеты отправлять пакет запроса.
 
 Раньше все писалось под QNX/POSIX threads и с этим проблем у меня не возникало, так как сокеты там потокобезопасные по умолчанию.
 
 Из дочерних потоков посылается сигнал в основной поток (который занимается обработкой данных)  Если надо отправить через сокет в потоке, то этому потоку с сокетом послать сигнал, по которому сокет будет отсылать данные.
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: Fregloin от Апреля 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.
 Название: Re: Несколько TCP клиентов в одной программе
 Отправлено: Fregloin от Апреля 28, 2011,  18:01
 
 В отладчике на this ругается : <unavailable synchroneous data>ощущение что стек покоцался...
 
 также связал сигнал дочернего сетевого потока и основного ГУИ с поправкой
 connect(mmnClientThread->mmnClient,SIGNAL(frameReceived(int)),this,SLOT(data_receive(int)),Qt::DirectConnection);
 
 все равно SIGSEGV.
 
 
 нашёл ошибку, банально копировал не в тот указатель, что надо...
 пока все работает.
 
 
 |