Название: Несколько 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.
нашёл ошибку, банально копировал не в тот указатель, что надо... пока все работает.
|