Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Reklats от Декабря 08, 2011, 01:27



Название: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 01:27
    Здравствуйте.
    Скоро будет неделя, как бьюсь над этой проблемой. Задача не тривиальна: создать сервер и каждое новое соединение обрабатывать в новом потоке. Проблема как раз с теми самыми потоками. Перелопатил кучу форумов (раз (http://www.prog.org.ru/topic_8844_0.html), два (http://ubray.ru/view.php?content=6&view=6)), примеры (threadedfortuneserver), но ничего подходящего не нашёл. Я только начинаю использовать потоки и сложные изъяснения естественно не понимаю.
    Сам сервер взял из книги Шлее "Qt4.5. Профессиональное программирование на C++". Многопоточность стал прикручивать из примера "threadedfortuneserver". Сервер соединения принимает, данные принимает \ отправляет, но при закрытии клиента, сервер кидает исключение и закрывается, да и потоки толком не работают. Я уже и не знаю, что делать.
    Предполагаю, что нужно использовать QObject::moveToThread, но вот где...
    Вот код основных мест:

QServerThread - класс потока, в котором обрабатывается соединение.
Код
C++ (Qt)
#ifndef QSERVERTHREAD_H
#define QSERVERTHREAD_H
#define NUMTOSTR(x) QString::number(x)
 
#include <QThread>
#include <QTcpSocket>
 
class QServerThread : public QThread
{
   Q_OBJECT
 
public:
   explicit QServerThread(int socketDescriptor, QObject *parent);
   ~QServerThread();
   void run();
 
private:
   int socketDescriptor;
   qint16 m_nNextBlockSize;
   QTcpSocket pClientSocket;
   QThread *parentThread;
   void sendToClient(const QString &nstr);
 
private slots:
   void slotReadFromClient();
   void slotDisconnect();
 
signals:
   void error(QTcpSocket::SocketError socketError);
   void print(const QString &info);
};
 
#endif // QSERVERTHREAD_H
 
//===============================
 
#define PRINT(x) emit QServerThread::print((QString)x);
 
QServerThread::QServerThread(int socketDescriptor, QObject *parent)
   : QThread(parent), socketDescriptor(socketDescriptor), parentThread(parent->thread())
{
   this->moveToThread(this);
}
 
QServerThread::~QServerThread(){
   delete parentThread;
}
 
void QServerThread::run(){
 
   if (!pClientSocket.setSocketDescriptor(socketDescriptor)) {
       emit error(pClientSocket.error());
       return;
   }
 
   //pClientSocket.moveToThread(this);
   PRINT("New connection from "+
         pClientSocket.peerAddress().toString() + ":" +
         NUMTOSTR(pClientSocket.peerPort()))
 
   sendToClient("HELLO WORLD");
 
   connect(&pClientSocket, SIGNAL(readyRead()), this, SLOT(slotReadFromClient()));
   connect(&pClientSocket, SIGNAL(disconnected()), this, SLOT(slotDisconnect()));
   connect(&pClientSocket, SIGNAL(destroyed()), this, SLOT(quit()));
 
   exec();
   this->moveToThread(parentThread);
}
 

QServerBase - класс сервера
Код
C++ (Qt)
class QServerBase : public QTcpServer
{
   Q_OBJECT
public:
   explicit QServerBase(QObject *parent = 0);
 
protected:
   void incomingConnection(int socketDescriptor);
 
signals:
   void log(const QString &info);
 
};
 
//========================
 
QServerBase::QServerBase(QObject *parent) :
   QTcpServer(parent)
{
}
 
void QServerBase::incomingConnection(int socketDescriptor){
   QServerThread *thread = new QServerThread(socketDescriptor, this);
   //thread->moveToThread(thread); in constructor
   connect(thread, SIGNAL(print(QString)), this, SIGNAL(log(QString)));
   connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
   thread->start();
}
 
 

Вывод консоли
Код
C++ (Qt)
QObject::moveToThread: Cannot move objects with a parent // on connect client
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x1412000), parents thread is QThread(0x627530), current thread is QServerThread(0x1411ff0)
Error - RtlWerpReportException failed with status code :-1073741823. Will try to launch the process directly // on disconnect client
 

UPD FIN: Для тех, кто не хочет искать решение по всей теме, вот готовые рабочие сорцы многопоточного сервера.


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 08, 2011, 07:20
Эм, можно вопрос?
А зачем вам exec(); в потоке ?

Список замечаний (пусть и невысокого я уровня, но насоветую):

1) - moveToThread используется, когда из основного(или другого) потока, вызываются открытые функции Вашего рабочего потока. (в других случаях он не нужен)
2) - moveToThread должен быть в 1 месте. В конструкторе потока. Зачем вы его вызываете в методе run, я не могу представить.
3) + moveToThread в конструкторе потока, у вас расположен верно :)
4) - Чтобы поток запустился и работал событийно (т.е. чтобы откликался на события), то нужно вызвать метод QThread::run();(в методе run потока)  (и exec тут не нужен)

К сожалению вы привели не весь код нитки(нету методов диссконекта и чтения), потому ничего более не могу посоветовать.





Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 09:26
После того, как run отработал moveToThread "обратно" делать не нужно, т.к. это ни на что не повлияет.
Сылку на parentThread можешь убрать, она тебе не нужна.

А крах во почему.
Код
C++ (Qt)
QServerThread::~QServerThread(){
   delete parentThread;
}
Зачем ты в деструкторе удаляешь родителя? Это антикутишный подход)

UPD это еще и не родитель, а указатель на поток родителя.
(вот родитель удивится :)


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 09:41
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x1412000), parents thread is QThread(0x627530), current thread is QServerThread(0x1411ff0)
Создавай сокет динамически внутри run и не придется ничего двигать.
Либо добавь pClientSocket.moveToThread(this);

Вообще по-моему лучший подход, это создавать все необходимые тебе в потоке классы, непосредственно в самом потоке (в run)


Название: Re: Простой многопоточный сервер
Отправлено: Igors от Декабря 08, 2011, 10:33
    Скоро будет неделя, как бьюсь над этой проблемой. Задача не тривиальна: создать сервер и каждое новое соединение обрабатывать в новом потоке.
Неделя еще не срок. Подход как раз тривиальный/банальный, "новое соединение - новый поток" именно это первое что приходит в голову. Но это не профессионально  :)


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 15:17
    Проблема всё ещё имеет место быть, хотя и частично исчезла. Сделал динамический сокет (собственно как и советовал Шлее в своей книге, а я его советом пренебрёг) и убрал все упоминания о родительском потоке parentThread. Теперь при закрытии клиента сервер не падает, а любезно информирует об отключении клиента. А вот уже при закрытии сервера кидается исключение - RtlWerpReportException и тут, я думаю, дело в потоках. К тому же this->moveToThread(this); (в конструкторе потока) не отрабатывает - пишет в консоль, что нельзя переместить объект с родителем  :-\. И пресловутое Cannot create children... осталось. Я всё ещё делаю что-то не так.

    Скоро будет неделя, как бьюсь над этой проблемой. Задача не тривиальна: создать сервер и каждое новое соединение обрабатывать в новом потоке.
Неделя еще не срок. Подход как раз тривиальный/банальный, "новое соединение - новый поток" именно это первое что приходит в голову. Но это не профессионально  :)
Я в курсе, что это не профессионально, но если небольшое количество потоков - можно. По уму надо делать пул потоков, хотя количество потоков и обусловлено производительностью машин, но да, не каждая машина потянет 1000 активных соединений-потоков. У меня и эта банальщина пока не работает  ;D.

Эм, можно вопрос?
А зачем вам exec(); в потоке ?
Для того, чтобы обрабатывать соединение - принимать \ отправлять данные. Без него (exec();) соединение закрывается сразу после коннекта ("привет" только отправляет клиенту и всё).

    И ещё. Когда я шлю серверу первую порцию данных, он пишет что принято [любое число] байт и выводит пустую строку. Все следующие данные принимает нормально. Тут я тоже в замешательстве.

Свежая порция исходников.
QServerThread, кроме него ничего не менял.
Код
C++ (Qt)
class QServerThread : public QThread
{
   Q_OBJECT
 
public:
   explicit QServerThread(int socketDescriptor, QObject *parent);
   void run();
 
private:
   int socketDescriptor;
   qint16 m_nNextBlockSize;
   //QTcpSocket *pClientSocket;
   void sendToClient(QTcpSocket *pClientSocket, const QString &nstr);
 
private slots:
   void slotReadFromClient();
   void slotDisconnect();
 
signals:
   void error(QTcpSocket::SocketError socketError);
   void print(const QString &info);
};
//====================================
QServerThread::QServerThread(int socketDescriptor, QObject *parent)
   : QThread(parent), socketDescriptor(socketDescriptor)
{
   this->moveToThread(this); // this line doesn`t work.
}
 
void QServerThread::run(){
 
   QTcpSocket *pClientSocket = new QTcpSocket(this);
 
   if (!pClientSocket->setSocketDescriptor(socketDescriptor)) {
       emit error(pClientSocket->error());
       return;
   }
 
   PRINT("New connection from "+
         pClientSocket->peerAddress().toString() + ":" +
         NUMTOSTR(pClientSocket->peerPort()))
 
   sendToClient(pClientSocket,"HELLO WORLD");
 
   connect(pClientSocket, SIGNAL(readyRead()), this, SLOT(slotReadFromClient()));
   connect(pClientSocket, SIGNAL(disconnected()), this, SLOT(slotDisconnect()));
   connect(pClientSocket, SIGNAL(destroyed()), this, SLOT(quit()));
 
   exec();
}
 
void QServerThread::sendToClient(QTcpSocket *pClientSocket, const QString &nstr){
   QByteArray dataBlock;
   QDataStream out(&dataBlock, QIODevice::WriteOnly);
   out.setVersion(QDataStream::Qt_4_0);
   out << qint16(0) << QTime::currentTime() << nstr;
   out.device()->seek(0);
   out << qint16(dataBlock.size() - sizeof(qint16));
 
   pClientSocket->write(dataBlock);
   PRINT("Sending data to "+
         pClientSocket->peerAddress().toString() + ":" + NUMTOSTR(pClientSocket->peerPort()))
}
 
void QServerThread::slotReadFromClient(){
   QTcpSocket *pClientSocket = (QTcpSocket*)sender();
 
   QDataStream in(pClientSocket);
   in.setVersion(QDataStream::Qt_4_0);
 
   for(;;){
       if(!m_nNextBlockSize){
           if (pClientSocket->bytesAvailable() < (int)sizeof(qint16)){
               break;
           }
           in >> m_nNextBlockSize;
       }
 
       if (pClientSocket->bytesAvailable() < m_nNextBlockSize){
           break;
       }
 
       QString data;
       QTime time;
       in >> time >> data;
 
       PRINT(pClientSocket->peerAddress().toString() + ":" + NUMTOSTR(pClientSocket->peerPort())+" has send " + NUMTOSTR(m_nNextBlockSize)+"b > " + data)
       m_nNextBlockSize = 0;
 
       sendToClient(pClientSocket, "Server Response > Received \"" + data + "\"");
   }
}
 
void QServerThread::slotDisconnect(){
   QTcpSocket *pClientSocket = (QTcpSocket*)sender();
   PRINT(pClientSocket->peerAddress().toString() + ":" + NUMTOSTR(pClientSocket->peerPort())+" is disconnected")
   pClientSocket->deleteLater();
}
 

Консоль
Код
C++ (Qt)
QObject::moveToThread: Cannot move objects with a parent // on new connection
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QServerThread(0x6dc038), parents thread is QThread(0x817530), current thread is QServerThread(0x6dc038)
Error - RtlWerpReportException failed with status code :-1073741823. Will try to launch the process directly // on close
 


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 15:47
Цитировать
пишет в консоль, что нельзя переместить объект с родителем  . И пресловутое Cannot create children... осталось. Я всё ещё делаю что-то не так.
Очевидно, что ты пытаешься переместить объект с родителем? )

убери вообще moveToThread он не нужен.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 15:49
Код
C++ (Qt)
QTcpSocket *pClientSocket = new QTcpSocket(this);
this убери.

Код
C++ (Qt)
QTcpSocket *pClientSocket = new QTcpSocket();

А где delete pClientSocket?

Вообще сделал бы на стеке и не парился.


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 17:17
    Спасибо, почти всё стало работать гладко. А вот при закрытии сервера всё равно кидается исключение.
    Вообще, использовать moveToThread меня надоумил вот этот (http://ubray.ru/view.php?content=6&view=6) форум. Вначале я писал без  :).
    Непонятно пару моментов:
1)
Код
C++ (Qt)
QTcpSocket *pClientSocket = new QTcpSocket();
Почему тут не надо отправлять указатель на родительский объект (поток)? При создании сервера это же делается.
Код
C++ (Qt)
m_pServerBase = new QServerBase(this);
This в данном случае указывает на главный поток.

2)
А где delete pClientSocket?
А разве это
Код
C++ (Qt)
void QServerThread::slotDisconnect(){
   QTcpSocket *pClientSocket = (QTcpSocket*)sender();
  //some code
   pClientSocket->deleteLater();
}
 
не удаляет сокет? И если нет, то где ставить? Переопределять метод quit()?

3)
Вообще сделал бы на стеке и не парился.
Можно поподробнее про это?


Название: Re: Простой многопоточный сервер
Отправлено: ufna от Декабря 08, 2011, 17:18
Вообще сервера на Erlang писать надо  ;D


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 19:18
Я не заметил deleteLater(). тогда ok.

Разница в том, что в 1 случае ты создаешь сокет в потоке, а this указывает на объект в другом потоке, о чем ты и получал предупреждение.

А во 2 случае обычные экземпляр класса и все как обычно.

Посмотри Threaded Fortune Server или какой-то пример. Как там создается сокет. Как раз на стеке.


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 20:27
andrew.k, плюсую в карму. Спасибо  :).

    Разобрался с исключениями при закрытии. Это я в главном потоке в деструкторе удалял много лишнего. Теперь многопоточность работает. Осталось разобраться почему первую партию данных сервер не принимает нормально. Хотя все последующие нормально принимает.
    И ещё у меня складывается такое впечатление, что память не освобождается должным образом. Судя по диспетчеру, при закрытии соединения что-то высвобождается, но не всё (прирост 120кб, убыль 20, итого 100кб осталось висеть от одного соединения (!)).

Цитата: andrew.k
Посмотри Threaded Fortune Server или какой-то пример. Как там создается сокет. Как раз на стеке.
    Пересмотрел ещё раз пример. Там в потоке (в методе run() потока) создаётся статически (?) сокет. И отсылка данных идёт в этом же потоке. Сразу при подключении. Я тоже создаю сокет, динамически правда. Но вот заметил одну вещь: коннекчусь я к слотам в дочернем потоке, а вот отрабатывают сами слоты уже в главном. Странно. Я думал, что весь класс - это отдельный поток, а оказывается только метод run().



Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 21:13
не статически, а на стеке.

да про слоты я забыл)
создавай свой поток без родителя, а в run добавь movetothread(this)
тогда слоты будут отрабатывать в потоке.


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 22:37
Слоты упорно не хотят запускаться в дочернем потоке.
Код
C++ (Qt)
this->moveToThread(this->thread());
Ставил и в констркуторе, и в QServerThread::run();, и с родителем, и без родителя...

Вот что вывела консоль :
Код
C++ (Qt)
QServerBase::QServerBase() 0x145c
QServerThread::QServerThread() 0x145c
QServerThread::QServerThread(QObject *parent) 0x145c  // parent thread id
QThread::run() 0x1504
QServerThread::slotReadFromClient() 0x145c
QServerThread::slotReadFromClient() 0x145c
 

Первый пакет данных по прежнему теряется.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 22:42
Ты разницу видишь?

movetothread(this)

this->moveToThread(this->thread());

??

Делать это надо в run


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 08, 2011, 22:48
Вижу. Мне консоль радостно сообщила, что текущий поток - это не поток объекта.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 08, 2011, 22:52
Вижу. Мне консоль радостно сообщила, что текущий поток - это не поток объекта.
Давай я завтра тебе отвечу. Если еще кто-нибудь. Сегодня уже трудно.


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 09, 2011, 00:51
    Всё. Домучил. :D Надо было вызывать
Код
C++ (Qt)
this->moveToThread(this);
в конструкторе потока. В самом первом комментарии мне про это и писали. А поток и сокет создавать без родителя.
    Теперь буду думать, почему первое слово съедает корова  :). Первая партия посланных данных почему-то не доходит... Да и при первом отключении клиента в памяти остаётся более 80кб от того количества кб, которое прибавилось при подключении клиента. При последующих коннектах\ дисконнектах всё высвобождается, причём при последующих коннектах \ дисконнектах прирост осуществляется на ~20кб. Очень и очень странно это всё.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 09, 2011, 01:23
Значит, я ошибся, сказав, что надо в run.
Поэтому я и хотел завтра посмотреть в своем коде. А то не помню уже.

А почему ты пишешь this->moveToThread,  а не просто moveToThread?

Завтра разберемся с твоими данными)


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 09, 2011, 07:32
Reklats, можно весь твой измененный код?
И клиента и сервера и потоков и слотов ?

Опасности:
1) this->moveToThread(this) и moveToThread(this), это одно и то же :) Просто второй вариант экономнее.
2) При первом посыле пакетов, ты проверяешь клиент на готовность получать данные? (если он у тебя тоже поточный, вполне возможно, что не инициализируется к тому времени)
3) Смотреть 1 пост и там есть кое-что  про событийный поток :) (использовал или нет?)


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 09, 2011, 11:59
1) Мне больше нравится мой вариант, т.к. он нагляднее, а компилятор всё равно всё не нужное выкидывает.
3) В смысле запуск exec()? Конечно, без него никак. Не, не использовал.

Вот весь мой код (клиент взял у Шлее):


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 09, 2011, 14:09
exec() конечно хорошо, но не тормозит ли он у тебя основной поток? Насколько я помню, он перехватывает события с основного потока :)
(хотя давно я его пытался туда всунуть, могу и не помнить подробностей )

Код посмотрю


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 09, 2011, 17:01
exec() конечно хорошо, но не тормозит ли он у тебя основной поток? Насколько я помню, он перехватывает события с основного потока :)
Вот о таких сообщениях я и говорил)

Сегодня просто некогда смотреть твой проект. Вон может Верес справится.

Вы же справитесь, Весес?


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 12, 2011, 10:22
Покопался в сорцах IMPOMEZIA Simple Chat 2, думал найду решение. Нет, там все подключения обслуживаются в одном потоке, однако. Всё ещё не знаю в чём проблема.


Название: Re: Простой многопоточный сервер
Отправлено: IMPOMEZIA от Декабря 13, 2011, 01:08
На самом деле во второй версии немного сложнее, используются 2 потока, один только обслуживает соединения, а второй только обрабатывает данные. Фактически они обмениваются только телами пакетов.

P.S. В ранних версиях действительно используется один поток на всё.
P.P.S. Почему ещё никто не сказал что концепция один поток на соединение не самая удачная, из-за затрат на переключение контекстов потоков при большом числе соединений.



Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 07:53
Невижу ничего плохого в 1 потоке на соединение. Это использовалось ранее и без особых осложнений.


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 07:59
Невижу ничего плохого в 1 потоке на соединение. Это использовалось ранее и без особых осложнений.
Лабораторная?


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 08:00
P.P.S. Почему ещё никто не сказал что концепция один поток на соединение не самая удачная...
Задолбало это говорить. :)


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 08:28
to BRE

Рабочий продукт. К сожалению ваше "задолбало" отсутсвует в ~90% тем про потоки.


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 08:32
Рабочий продукт.
А сколько этот сервер обслуживает клиентов одновременно?

К сожалению ваше "задолбало" отсутсвует в ~90% тем про потоки.
Конечно, это есть во многих темах про серверы.


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 09:13
Пока ни 1 не видел :) Сервер маленький, функционал небольшой.


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 11:22
Пока ни 1 не видел :)
Так ты посмотри темы, которые поднимались раньше твоего прихода. ;) Тогда тоже по чуть-чуть пописывали. :)

Сервер маленький, функционал небольшой.
Так ты нагрузи его как следует и все поймешь.


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 12:45
Нагрузка на нем достаточная. Темы смотрел (ибо чтот давненько не поднимали тем про серверы, лазил в архив почти что).


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 12:47
Нагрузка на нем достаточная.
Достаточная для чего? :)
Цифры.... где цифры? Сколько клиентов он обслуживает одновременно? :)


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 13:10
Темы смотрел (ибо чтот давненько не поднимали тем про серверы, лазил в архив почти что).
http://www.prog.org.ru/topic_16415_0.html
http://www.prog.org.ru/topic_13804_0.html
http://www.prog.org.ru/topic_7889_0.html
Дальше не искал, темы где просто написано "Лучше так не делать" пропускал.
В подписи у себя напиши "Не умею пользоваться поиском!" ;)


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 13:23
BRE, себе в подписи напиши - "не умею помогать людям" и "говорю общими словами."

Моя подпись меня устраивает, тем более что нигде не обсуждалась нагрузки, ВАМ так не кажется?

Темы перечисленные вами:

Алгоритм высоконагруженного сервера.  Январь 19, 2011, 10:16
Высоконагруженный сервер на Qt, реально ли?  Октябрь 19, 2008, 21:39
Вариант написания многопоточного сервера - динамические потоки или пул!? Июнь 01, 2010, 21:57

Первые 2 не подходят по теме, третий имеет выраженный вопрос.

Темы, предъявленные вами не проходят критики, кроме последней, но которая видимо запрятана слишком глубоко. На какой из 38 страниц выдачи поиска по "сервер", она находится?

update: ВЫ что то сказали по поводу поток на общение и обработку плохо. Что то нигде не видно таких слов. увы. Уточняю - для обычного сервера, а не высоконагружаемого.


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 13:28
BRE, себе в подписи напиши - "не умею помогать людям" и "говорю общими словами."
Ну прости.

PS Моя подпись меня устраивает, тем более что нигде не обсуждалась нагрузки, ВАМ так не кажется?
Ну я уже два раза спрашивал про нагрузку на твой сервер... как-то не обсуждается. :)


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 13:34
Нагрузка, нагрузка. Какая разница, если Вы говорите об архитектуре программы?

Применяя QString я знаю, что оно тратит ресурсов больше, чем std::string. А чем Вы аргументируете?

Не увидел ни 1 дельного комментария, окромя - нагрузи и всё поймешь. нагрузил. В моем конкретном случае, работа идет стабильно, без сбоев, быстродействие устраивает.

Загадка - что плохо?

PS создавать ради прикола 10к клиентов, нет на это мощности и времени. Аргументируйте точнее, что и как плохо. Вы же советы пришли давать, а не отнекиваться.


Название: Re: Простой многопоточный сервер
Отправлено: ufna от Декабря 13, 2011, 13:42
Верес,

Если у тебя клиентов с малой нагрузкой штук 100 и для тестов - вообще не парься, можно хоть по десять потоков на клиента :)

А если это будет production сервер, который обслуживает хотя бы 4к клиентов, очень сильно рекомендую понять, что это "нагруженный" сервер.

Архитектура ПО зависит от его целей.

Нагрузку стоит делать роботом, и по 10к, и по более юзеров.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 13, 2011, 13:44
Нагрузка, нагрузка. Какая разница, если Вы говорите об архитектуре программы?

Применяя QString я знаю, что оно тратит ресурсов больше, чем std::string. А чем Вы аргументируете?

Не увидел ни 1 дельного комментария, окромя - нагрузи и всё поймешь. нагрузил. В моем конкретном случае, работа идет стабильно, без сбоев, быстродействие устраивает.

Загадка - что плохо?

PS создавать ради прикола 10к клиентов, нет на это мощности и времени. Аргументируйте точнее, что и как плохо. Вы же советы пришли давать, а не отнекиваться.
Это то, о чем я говорил в теме про "Вы". Важно не что, а важно, как ты напишешь.


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 13:49
to ufna спасибо, и я это понимаю.
to andrew.k - флудеров на кол!

Вопрос ставился исключительно BRE. Тема из "как сделать сервер", переросла его усилиями в "как сделать нагруженный сервер".

Я не спорю, лучше делать сразу с расчетом на максимальную нагрузку, НО. Никаких фактов и прочего он не привел. Я могу поверить на слово Пантеру, andrew.k, ufne, но в любом случае я попрошу объяснений, а как лучше сделать?



Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 13:56
Bepec ты попробуй не просто заголовки тем прочитать...


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 14:11
BRE, ты подпись еще не сделал?

"говорю общими словами."

Прочитал. Обсуждения интересные, даже про несколько десятков тысяч потоков речь шла, и прочая. Прочитал я их. НО...
Нету там совета как строить сервер. Как разбивать лучше потоки, общих рекомендаций тоже нет. Отдельные решения отдельных проблем - сколько угодно. Но мы то говорим о структуре?

Не можешь сформулировать мысль, так и скажи. Можешь и не хочешь - скажи. Вздыхаешь и говоришь, что молодежь пошла всякая - так и говори, но не мне. Мне скажи просто - разбирайся сам, я помогать не хочу.

Кстати интересный факт. Вещи, о которых спрашивают почти все новички, нигде не сконцентрированы. Они есть в десятках разных тем, но и там, и там по крупицам.
Конечно я понимаю, что Пантер не может всё читать, собирать, делать мануалы. У него и других дел полно. Но можно ведь в конце каждой темы писать общее содержание и решение?

Сейчас уже более менее начали хоть [РЕШЕНО] ставить. Но не более того.

BRE, если ВЫ скажете, что в процессе обсуждения всех проблем имеется решение - то пожалуйста, несложно найти тему с 20 решениями, из которых 1 верное, и написанное на 3 странице из 6.


Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 15:06
Нету там совета как строить сервер. Как разбивать лучше потоки, общих рекомендаций тоже нет. Отдельные решения отдельных проблем - сколько угодно. Но мы то говорим о структуре?
Ааа, ты искал тему "Вот так нужно делать серверы!", где в первом сообщении перечислены 10 правил хорошего сервера... Еще раз извини, не успели сделать.
Кому это нужно - читает книги, статьи, обсуждения, пробует/экспериментирует сам, и обсуждает это на форуме.
В приведенных темах есть ссылки на статьи и мысли на вроде "а пулингом/приемом/передачей может заниматься и один поток для всех клиентов" или "а для выполнения работы подойдет пул потоков...". Это то, что должно указать заинтересованному читателю направлению дальнейших изысканий...

Кстати интересный факт. Вещи, о которых спрашивают почти все новички, нигде не сконцентрированы. Они есть в десятках разных тем, но и там, и там по крупицам.
Да, потому что это форум где никто никому ничего не должен, здесь люди общаются. И задача новичка найти эти крупицы (получив кучу других полезных знаний), а не получить готовые решения для копипаста.


Название: Re: Простой многопоточный сервер
Отправлено: Bepec от Декабря 13, 2011, 15:31
Эх, BRE, печально что Вы так рассуждаете.
Новичок должен найти крупицы. Сложить знание как головоломку. Как хорошо, что умные люди пишет статьи и пособия, и думают иначе.
Ваш подход как раз подход копипаста. Найти отдельное решение отдельной проблемы - и скопировать.

    Мой подход - дать информацию к размышлению. Список правил построения сервера - Вы его для себя составили я думаю, НО... Не хотите сформулировать и выложить, ибо Вы искали и Вы написали кучу программ. (метод собака на сене очень подходит)
    Дать информацию, но не решение - это хорошо. Я в любом случае наклепаю 10-20-40 примеров, тестируя и проверяя. Но Вы мне говорите - "да так, нечего тебе об архитектуре говорить, давай лучше ты скопипастишь вот этот пример, и разберешь отдельную проблему". А я говорю об архитектуре.
    Что писать надо функции класса приватными, публичные методы констовые(для возврата данных), отступы табовые.
А Вы в данном случае не хотите сформулировать 2 предложения, без ссылок и файлов.
     Можно даже уложиться и в 1. Я и сам найду, достану, разберусь, но нужно на что-то опираться в начале. Я(да и вот ТС) буду клепать такие же сервера как сейчас, до тех пор пока не возникнет "отдельная проблема", а за ней еще и еще. И опять я полезу на форум и создам уже сам тему "Почему, Как, Зачем".

     Можно было тогда вообще не писать вашей реплики "это плохо и меня заканало это писать". Толку что? Что я написал пару толстых комментов, что Вы ответили мне тем же, не затронув проблемы. Тем что испортили тему флудом?

Следующая реплика ТС - а как правильно делать? Тот же вопрос задал и я, ответа не получил.

PS Если Вы так боитесь формулировать свои принципы, мысли или же не желаете их раскрывать, то  не стоило и писать.



Название: Re: Простой многопоточный сервер
Отправлено: BRE от Декабря 13, 2011, 15:48
Можно было тогда вообще не писать вашей реплики "это плохо и меня заканало это писать".
Я правда жалею.


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 13, 2011, 18:49
    С удовольствием выложу рабочие исходники своего сервера и приписку к названию сделаю, когда разберусь с проблемой. Или кто-нить поможет.

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

    Согласен с идеей, что концепция "одно соединение - один поток" не правильна, ровно как и "все соединения в одном потоке", т.к. не обеспечивает нужного быстродействия. Думаю, что нужно в каждом потоке обслуживать некоторое число N соединений, а потом при превышении этого числа запускать новый поток. Думаю, что это будет компромиссом.


Название: Re: Простой многопоточный сервер
Отправлено: andrew.k от Декабря 13, 2011, 19:18
правильно думаешь, выложи еще раз в zip, если не сложно.


Название: Re: Простой многопоточный сервер
Отправлено: Igors от Декабря 13, 2011, 19:26
Думаю, что нужно в каждом потоке обслуживать некоторое число N соединений, а потом при превышении этого числа запускать новый поток. Думаю, что это будет компромиссом.
Это также обсуждалось, и уязвимые места довольно очевидны: трудно определить "время X" когда новая нитка должна быть создана, так же как и "время Y" когда от нее избавиться.

Никогда не имел никакого желания писать "многопоточный сервер", но, на мой взгляд, все multi-threaded задачи имеют много общего. Чем занимаюсь я в своей предметной области - это "нарезкой на порционные куски", выделением под-задач чтобы они могли быть скормлены любой рабочей нитке, их число = числу ядер. Это более 90% работы. Но есть еще задачи типа "проверить а не случилось ли чего" - и если да то подкинуть задания рабочим (типа диспетчера). Такие задачи нужно сливать в 1 нитку (ну или в как можно меньшее их число).

Повторюсь, что веб и сервера - не мой профиль, это просто "взгляд со стороны"  


Название: Re: Простой многопоточный сервер
Отправлено: Reklats от Декабря 13, 2011, 19:55
Это также обсуждалось, и уязвимые места довольно очевидны: трудно определить "время X" когда новая нитка должна быть создана, так же как и "время Y" когда от нее избавиться.
При каждом новом конекте, дисконнекте.

andrew.k, сорцы вот: http://pastebin.com/n0rM9Ncs (http://pastebin.com/n0rM9Ncs)
И тут ещё: http://www.prog.org.ru/index.php?topic=20287.msg138126#msg138126 (http://www.prog.org.ru/index.php?topic=20287.msg138126#msg138126)

UPD FIN: Решение найдено! У меня нулевое значение m_nNextBlockSize не задается вначале.
Какая же ламерская ошибка...

Код
C++ (Qt)
QServerThread::QServerThread(int socketDescriptor, QObject *parent)
   : QThread(parent), socketDescriptor(socketDescriptor), m_nNextBlockSize(0)
{
   this->moveToThread(this);
}
 


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 13, 2011, 20:47
m_nNextBlockSize где у тебя инициализируется?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 14, 2011, 13:15
Ну вот. Рано радовался.

Какие-то странные пакеты шлёт сервер. Ну и клиент тоже. Вот:
(http://cs5666.vkontakte.ru/u49529832/142142035/z_3946af26.jpg)

1) Такой пакет шлёт клиент
2) Таким отвечает сервер
3) Такой пакет не ловится, а нужно обработать именно такой. Только регистрируется, что клиент что-то послал.

Что посоветуете?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Bepec от Декабря 14, 2011, 13:53
А что ты посылал? в 3 пакетике?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 13:59
Вот ты бы там где у тебя break'и стоят добавил бы qDebug() какой-нибудь. Либо сам в отладке посиди.
Сразу станет понятно. В чем проблема.
Может размер неправильно считается и данных реально меньше, чем указано в размере.


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 14, 2011, 14:15
А что ты посылал? в 3 пакетике?
Третий пакет посылает клиент чата, протокол которого надо отдебажить, а в идеале - переписать сервер под Линух, т.к. автор зажал сорцы и даже просто протокол  :( А в пакете, скорее всего, посылается публичный ключ шифрования.

Может размер неправильно считается и данных реально меньше, чем указано в размере.
Точно, почему-то считывает "308" и соответственно во втором бряке выходит из цикла. Откуда он берет 308 - хз...


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 14:19
Ну так ты же сам говоришь, что в третий пакет тебе кто-то другой прислал.
С чего ты взял, что первые четыре байта у него длина пакета будет?
Тебе надо разбираться в его формате, тогда ты сможешь с ним работать.
Что ты вообще хочешь с ним сделать, если ты не знаешь, как он устроен?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Bepec от Декабря 14, 2011, 14:46
Reklats, а в твой протокол входит передача времени посыла? Или это эволюция твоей программы?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 14, 2011, 14:50
Reklats, а в твой протокол входит передача времени посыла? Или это эволюция твоей программы?
Нет, не входит. Это лишнее, я это выкинул. Можно сказать, что это эволюция.

Пытаюсь вот побайтно считывать данные из сокета...


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 14:56
По-моему проще свой чат с нуля написать, чем ковырять чей-то протокол самопальный.
В чем смысл?


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Bepec от Декабря 14, 2011, 15:00
andrew.k не соглашусь частично.

Проще написать свой протокол ^^.


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 14, 2011, 15:05
По-моему проще свой чат с нуля написать, чем ковырять чей-то протокол самопальный.
В чем смысл?

В обратной совместимости. Да и влом целый чат писать. Тут всего-то - протокол хакнуть и сервер сделать.

P.S  Что я делаю не так? Вывод пустой (данных нет):
Код
C++ (Qt)
   char *byte[10];
   for(int i = 0; i < 10; ++i){
       in >> byte[i];
       qDebug() << "Byte " << i << "is" << byte[i];
   }
 


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 15:06
andrew.k не соглашусь частично.
Соглашусь частично (http://lurkmore.to/Всем_похуй).

ЗЫ. Я собственно это и имел ввиду.


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: BRE от Декабря 14, 2011, 15:16
Тут всего-то - протокол хакнуть и сервер сделать.
Ну да, ну да....

А что это за указатели ты тут пытаешься прочесть?
Код
C++ (Qt)
   char *byte[10];
   for(int i = 0; i < 10; ++i){
       in >> byte[i];
       qDebug() << "Byte " << i << "is" << byte[i];
   }
 


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 15:22
Тут всего-то - протокол хакнуть и сервер сделать.
Ну да, ну да....

А что это за указатели ты тут пытаешься прочесть?
Код
C++ (Qt)
   char *byte[10];
   for(int i = 0; i < 10; ++i){
       in >> byte[i];
       qDebug() << "Byte " << i << "is" << byte[i];
   }
 

Точняк. Думаю требуется пояснение. Ты объявил массив указателей (char *byte[10]) и в них пишешь что-то, не выделив под него память.
Юзай readRawData что ли.


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Bepec от Декабря 14, 2011, 15:27
andrew.k  не груби незнакомым, не позорь папку с мамкой :)




Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: andrew.k от Декабря 14, 2011, 15:33
переименовал бы что ли тему обратно, убрал бы решено )


Название: Re: [РЕШЕНО] Простой многопоточный сервер
Отправлено: Reklats от Декабря 14, 2011, 21:38
переименовал бы что ли тему обратно, убрал бы решено )
Ну основное то решено) А это уже бонусом)

Всё, прочитал вроде как) Накатал функцию:

Код
C++ (Qt)
QString QServerThread::packetReader(QDataStream &stream){
   QString packet;
   quint8 sym(0);
 
   for(;;){
       stream >> sym;
       if(!sym) break;
       packet += (sym > 4) ? QString(QChar(sym)) : ".";
   }
 
   return packet;
}