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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: QThread + QTcpSocket + основной поток  (Прочитано 25689 раз)
Vexator
Гость
« : Август 04, 2009, 08:38 »

Доброго времени суток!

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

почитал, посмотрел примеры реализации подобных серверных приложений и решил сделать используя QThread + QTcpSocket. создав основной класс от QTcpServer , и порождая новые нити при каждом новом подключении.
в итоге получил нечто вида:
Код:
void chat_deamon::incomingConnection(int socketDescriptor)
{
chat_trhead *thread = new chat_trhead(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(post(QString)), this, SLOT(post(QString)) );
connect(this, SIGNAL(users_upd(QString)) , thread, SLOT(upd_users(QString)) );
thread->start();
}
а в нити:
Код:
void chat_trhead::run()
{
QTcpSocket tcpSocket;
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
return;
}

while(1)
{
// читаем и т.д.
}
при этом для общения с нитями использую связки из слотов/сигналов
возводя при этом флаги и обрабатывая их в цикле while(1) в run()
Код:
void chat_trhead::upd_users(QString nul)
{
ul = nul;
upd_u = true;
}

и все это работает, причем вроде даже хорошо, но:
1) передавать данные через сигналы не очень удобно, можно ли для этого передавать указатель на общие данные и писать/читать уже оттуда используя QRWlock(), не сильно ли это скажется на надежности и скорости приложения?
2) если к примеру требуется передать данные через сигнал КОНКРЕТНОМУ приемнику, как это сделать? ведь connect в данном случае привязывает слоты КАЖДОГО потока к одному СИГНАЛУ сервера, и следовательно, при его использовании данные рассылаются всем нитям, а не конкретной, можно ли как то ее идентифицировать и общаться с ней?
3) можно ли создать поток обработки QTcpSocket в отдельной нити так, что бы он обрабатывался не через while(1)... а через сигналы readyRead() и т.д. а то после окончания run() достучаться до потока уже немогу ( ...

подскажите как лучше сделать?
« Последнее редактирование: Август 04, 2009, 08:41 от Vexator » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1 : Август 04, 2009, 10:49 »

Цитировать
3) можно ли создать поток обработки QTcpSocket в отдельной нити так, что бы он обрабатывался не через while(1)... а через сигналы readyRead() и т.д. а то после окончания run() достучаться до потока уже немогу (
мож сделать так:
Код:
void chat_trhead::run()
{
    QTcpSocket tcpSocket;
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(тут какой нить слот));
    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket.error());
        return;
    }
    exec();
}
Записан

ArchLinux x86_64 / Win10 64 bit
Winstrol
Гость
« Ответ #2 : Август 04, 2009, 14:07 »

3) можно ли создать поток обработки QTcpSocket в отдельной нити так, что бы он обрабатывался не через while(1)... а через сигналы readyRead() и т.д. а то после окончания run() достучаться до потока уже немогу ( ...
Для работы через сигналы readyRead() надо либо не переопределять метод run, либо, совершив некоторые предварительные действия в переопределенном run, вызвать цикл обработки событий (в том числе от сокетов) - метод exec().
Записан
Vexator
Гость
« Ответ #3 : Август 05, 2009, 01:45 »

Спасибо буду тестить Улыбающийся
а что можете посоветовать по поводу того как обратиться к конкретному потоку (через сигнал) ?
« Последнее редактирование: Август 05, 2009, 01:53 от Vexator » Записан
alexman
Гость
« Ответ #4 : Август 05, 2009, 07:52 »

1) Можно использовать глобальный буфер, но его внутренние данные которые изменяются потоками необходимо закрыть мьютексом или семафором в зависимости от ситуации.
2) Можно реализовать глобальный proxy-класс, который будет отфильтровывать сообщения и посылать сигналы требуемым потокам.
3) while(1) плохо, так как процессор грузиться. Лучше использовать while(1), но внутри засыпать на QWaitCondition, а далее постоянно будить поток по сигналу readyRead.
Записан
Vexator
Гость
« Ответ #5 : Август 05, 2009, 08:01 »

1) Можно использовать глобальный буфер, но его внутренние данные которые изменяются потоками необходимо закрыть мьютексом или семафором в зависимости от ситуации.
по сути сделать общие ресурсы и управлять через них?..

2) Можно реализовать глобальный proxy-класс, который будет отфильтровывать сообщения и посылать сигналы требуемым потокам.
а как такое сделать??

3) while(1) плохо, так как процессор грузиться. Лучше использовать while(1), но внутри засыпать на QWaitCondition, а далее постоянно будить поток по сигналу readyRead.
я думал так сделать, но ,во-первых, у меня таких Condition может быть несколько (поток просыпается не только по получению readRead() но и других сигналов), можно ли это увязать в пределах QWaitCondition ??, а, во-вторых, смущает строчка из assistenta :
Цитировать
void QWaitCondition::wakeOne ()
Wakes one thread waiting on the wait condition. The thread that is woken up depends on the operating system's scheduling policies, and cannot be controlled or predicted.
If you want to wake up a specific thread, the solution is typically to use different wait conditions and have different threads wait on different conditions.
не будет ли проблем разбудить "конкретный" поток таким путем??
Записан
ритт
Гость
« Ответ #6 : Август 05, 2009, 08:09 »

1. жесть
2. жесть
3. плохо. либо waitForConnected, либо петлю

на connectNotify запоминай кто коннектился к сигналу (на disconnectNotify убирай запись из списка), когда узнал кому "отправлять сигнал" (видимо, данная задача это подразумевает), делай QMetaObject::invokeMethod
советую использовать петлю.

зы. и далеко не всегда поток на сокет - хорошее решение
Записан
Vexator
Гость
« Ответ #7 : Август 05, 2009, 08:15 »

1. жесть
2. жесть
ага )

3. плохо. либо waitForConnected, либо петлю
на connectNotify запоминай кто коннектился к сигналу (на disconnectNotify убирай запись из списка), когда узнал кому "отправлять сигнал" (видимо, данная задача это подразумевает), делай QMetaObject::invokeMethod
советую использовать петлю.
петлю - в смысле while(1) ?

зы. и далеко не всегда поток на сокет - хорошее решение
а как еще можно обеспечить параллельную работу основного потока (вычисления) с передачей в него и из него данных в сокеты ?
Записан
BRE
Гость
« Ответ #8 : Август 05, 2009, 08:28 »

Рекомендую прочесть статейку:
http://www.opennet.ru/base/dev/server_way.txt.html
Записан
alexman
Гость
« Ответ #9 : Август 05, 2009, 08:40 »

1. Не жесть, а часто используемое решение.
2. На счёт этого согласен)
Записан
Vexator
Гость
« Ответ #10 : Август 05, 2009, 08:43 »

1. Не жесть, а часто используемое решение.
ага с этого начинал )

2. На счёт этого согласен)
мне в принципе интересно как ето сделать )))
Записан
ритт
Гость
« Ответ #11 : Август 05, 2009, 10:13 »

Код:
 class MyThread : public QThread
 {
 public:
     void run();
 };

 void MyThread::run()
 {
     QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);
     exec();
 }
Записан
Fastman
Гость
« Ответ #12 : Август 05, 2009, 23:10 »

У меня например сделан отдельный класс для обработки сообщений.
Все как и выше практически:

Сам сервак который слушает клиентов:

Код:
void CTcpCast::StartServer(qint32 nPort)
{
if (!listen(QHostAddress::Any, nPort))
return;
}

void CTcpCast::StopServer()
{
this->close();
}

void CTcpCast::incomingConnection(qint32 nSocketDescriptor)
{
CThreadWorker *m_pThread = new CThreadWorker(nSocketDescriptor, this);
connect(m_pThread, SIGNAL(finished()), m_pThread, SLOT(deleteLater()));
m_pThread->start();
}

Поточный класс:
Код:

CThreadWorker::CThreadWorker(int nSocketDescriptor, QObject *parent)
: QThread(parent), m_SockedDescriptor(nSocketDescriptor)
{

...
}

void CThreadWorker::run()
{
CConnection m_Connection;
if(!m_Connection.setSocketDescriptor(m_SockedDescriptor))
return;

connect(&m_Connection, SIGNAL(disconnected()), this, SLOT(deleteConnection()));
exec();
}

А вот CConnection:
 
Код:
CConnection::CConnection(QObject *parent)
: QTcpSocket(parent)
{
connect(this, SIGNAL(readyRead()), this, SLOT(processReadyRead()));
             ...
}
....
void CConnection::processReadyRead()
{
char    *szData;
QString cReadedData;

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);

if(this->isReadable())
{
qint64 qnSize = this->bytesAvailable();
if( qnSize > 0)
{
szData = new char [qnSize+sizeof(szData)];
qint64 qnReadSize = this->read(szData, qnSize);
szData[qnReadSize] = NULL;
}
}

cReadedData.append(szData);
             if(szData != NULL)
    delete[] szData;

qDebug() << cReadedData.simplified();
             ....
}
« Последнее редактирование: Август 05, 2009, 23:11 от Fastman » Записан
Vexator
Гость
« Ответ #13 : Август 18, 2009, 06:41 »

возник новый вопрос...
а как правильно организовать работу с общей памятью для множества процессов?
создать отдельный класс для хранения данных и передать всем на него указатель?
при каждом обращении к данному классу необходимо делать QMutex ?
подскажите пожалуйста, а то что то завис  Непонимающий
Записан
denka
Гость
« Ответ #14 : Август 18, 2009, 08:40 »

Глянь вот это http://doc.trolltech.com/4.4/qsharedmemory.html
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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