Russian Qt Forum

Qt => Работа с сетью => Тема начата: G-virus от Февраль 19, 2010, 12:03



Название: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 12:03
Привет всем. Пишу сервер, обслуживающий два клиента отдельно. Порылся на форуме, говорят что реализуется с помощью QThread. А можно ли как-нибудь сделать по другому, не используя класс QThread? Допустим создать два экземпляра QTcpSocket и открыть два порта. Пытался, но не получается. У меня в книге описана только двусторонняя работа (один серв, один клиент)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 12:09
А можно ли как-нибудь сделать по другому, не используя класс QThread?
Можно. Объект QTcpServer может обслуживать несколько подключений на одном порту и для каждого клиента создавать свой канал.

Пытался, но не получается. У меня в книге описана только двусторонняя работа (один серв, один клиент)
Показывай как пытался.  ;)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 12:20
Сильно не ругайте, если говнокод написал ;D
Вот main.cpp
Код
C++ (Qt)
#include <QtGui/QApplication>
#include "widget.h"
#include <QTextCodec>
#include <QMessageBox>
int main(int argc, char *argv[])
{
   QTextCodec *codec = QTextCodec::codecForName("cp1251");
   QTextCodec::setCodecForLocale(codec);
   QTextCodec::setCodecForTr(codec);
   QTextCodec::setCodecForCStrings(codec);
   QApplication a(argc, argv);
   QWidget w;
   w.setGeometry(0, 0, 0, 0);
   w.show();
   TripServer server;
   if( !server.listen( QHostAddress::Any, 6178 ) )
   {
       QMessageBox::critical(NULL, "Error", "Ошибка открытия порта 6178");
           return 1;
   }
   TripServer server2;
   if( !server2.listen( QHostAddress::Any, 6177 ) )
   {
       QMessageBox::critical(NULL, "Error", "Ошибка открытия порта 6178");
           return 1;
   }
return a.exec();
}
 
 

widget.h
Код
C++ (Qt)
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QTcpServer>
#include <QTcpSocket>
 
class ClientSocket : public QTcpSocket
{
   Q_OBJECT
public:
   ClientSocket(QObject *parent = 0);
       int id;
 QString strA;
private slots:
   void readClient();
};
class TripServer : public QTcpServer
{
   Q_OBJECT
public:
   TripServer( QObject *parent = 0 ) : QTcpServer( parent ){}
private:
   void incomingConnection(int socketId);
};
#endif // WIDGET_H
 

widget.cpp
Код
C++ (Qt)
#include "widget.h"
 
void TripServer::incomingConnection(int socketId)
{
   ClientSocket *socket = new ClientSocket(this);
   socket->setSocketDescriptor(socketId);
 
}
 
ClientSocket::ClientSocket( QObject *parent )
       :QTcpSocket(parent)
{
   QObject::connect( this, SIGNAL( readyRead() ), this, SLOT( readClient() ) );
   QObject::connect( this, SIGNAL( disconnected() ), this, SLOT( deleteLater() ) );
}
void ClientSocket::readClient()
{
   QDataStream in(this);
   QDataStream out(this);
   in.setVersion(QDataStream::Qt_4_5);
   in >> strA;
   id = 1;
   if (strA == "Hi")
           out << id;
}
 

Вот каким-то таким образом пытался сделать  ;D Но все равно это не правильно.... :(


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 12:53
Не обязательно создавать несколько объектов, слушающих разные порты. Хватит и одного.
Не вижу самого клиента, как отправляешь данные?
При чтении (слот readClient) нужно дожидаться момента прихода необходимых данных, а только потом их читать и разбирать. В примерах все это есть, посмотри (bytesAvailable).


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 13:43
Не обязательно создавать несколько объектов, слушающих разные порты. Хватит и одного.
Не вижу самого клиента, как отправляешь данные?
При чтении (слот readClient) нужно дожидаться момента прихода необходимых данных, а только потом их читать и разбирать. В примерах все это есть, посмотри (bytesAvailable).


Клиент у меня создает объект QTcpSocket подключается к указанному ip и порту ну и передача данных, думаю это элементарно :) Создается QDataStream out, и при нажатии кнопки отсылается данные (извиняюсь за словесное объяснение - не со своего компьютера сижу, кода нету чтобы послать). Просто при реализации сервера обслуживающего только 1 подключение, там проще. Создается 1 объект QTcpServer, и начинается прослушка порта. Как подключается клиент, слот readClient() ловит входящие пакеты. Все это прекрасно и замечательно, но только в случае с 1 подключением


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 13:50
Подключайся несколькими клиентами к одному порту сервера. Он отработает и создаст несколько QTcpSocket для обмена с каждым клиента. И они будут асинхронно (с некоторыми оговорками) работать.
Что именно не так?  ;)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 14:25
Подключайся несколькими клиентами к одному порту сервера. Он отработает и создаст несколько QTcpSocket для обмена с каждым клиента. И они будут асинхронно (с некоторыми оговорками) работать.
Что именно не так?  ;)
Я так уже пробовал, но не получается сделать так, чтобы сервер принял данные одного клиента, обработал, и передал их на другой. Может что-то забываю?
Точнее я так подумывал, как сделать отдельный QDataStream out для второго сервера (пробовал указать QDataStream out(server1) и QDataStream out(server2) но компилятор давал ошибку)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 15:15
Что-то я запутался что ты хочешь сделать.  :)
Есть один сервер QTcpServer, он слушает нужный порт (6178). Когда к этому серверу подключается клиент для него создается отдельное подключение ClientSocket, через которое происходит общение: сервер <---> клиент1.
Если, к этому же серверу, через тот-же порт, подключается второй клиент, то для него создастся другое подключение ClientSocket: сервер <---> клиент2.
Эти два подключения будут работать каждый с своим клиентом.


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 15:23
Что-то я запутался что ты хочешь сделать.  :)

Сори, толком не объяснил задачу. Имеется транслятор(сервер). Он принимает данные от одного клиента, обрабатывает, и передает их другому клиенту


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 15:48
А как сервер определяет, кому что отдавать?
Например, есть 25 клиентов, они подключились к серверу, как он определяет от кого данные получать и кому отдавать?


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 16:06
А как сервер определяет, кому что отдавать?
Например, есть 25 клиентов, они подключились к серверу, как он определяет от кого данные получать и кому отдавать?
Хм... а хз.... А если допустим сделать чтобы серверу все равно было от кого принимать, главное отдать определенному?


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 19, 2010, 16:32
Хм... а хз.... А если допустим сделать чтобы серверу все равно было от кого принимать, главное отдать определенному?
Хм.  ::)
Тогда можно сделать, как ты и пробовал.
Сервер открывает два порта (например 6177 и 6178). Все кто подключаются к первому порту - отправляют данные (Sender), все кто подключаются ко второму - получают результат (Recv).
Напиши класс ProccessData. Объект этого класса будет заниматься обработкой данных. Он будет один для всех (singleton).
Когда, от Sender-клиента приходят данные они помещаются в очередь объекта ProcessData, который по очереди их обрабатывает. Если данные готовы, он отсылает сигнал с готовыми данными. Объекты Recv-клиентов, этот сигнал ловят и отправляют их по сети.

InTcpServer (6177)          ProcessData            OutTcpServer (6178)
   InClient1
   InClient2      -->       обработка      -->      OutClient1
   InClient3
   InClient4



Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 19, 2010, 16:55
У меня получилось вроде бы правильно присоединить оба клиента. Но возник вопрос. Если я создаю два объекта QTcpServer ина разные порты. Как мне привязать QTcpSocket и QDataStream out или in в отдельности для каждого клиента?


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: mcrads от Февраль 20, 2010, 18:46
Что-то судари вы заморачиваетесь. Посмотрите внимательно Fortune Client & Fortune Server в демках. там даже не создается новый класс наследуемый от QTcpSocket, все можно сделать стандартный сервер. смотрим например так.

QTcpServer *server = new QTcpServer
QTcpSocket *socket1;
QTcpSocket *socket2;
int i = 0;
protected void server::incomingConnection()
{
    ++i;
    if (i == 2)
    {
        socket1 = server->nextPendingConnection();
        socket2 = server->nextPendingConnection();
    }
}
все, для каждого подключения мы создали свой сокет! и потом обращаемся к нужному по мере необходимости - откуда прочитать, куда послать =)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: BRE от Февраль 20, 2010, 18:58
Прочитай весь тред. ;)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: break от Февраль 21, 2010, 03:02
Чтобы сервер разобрался с 25 клиентами у каждого клиента должен быть ID, а слушать можно 1 порт. Тогда если изменения пришли с клиента с ID=5 передать это изменение всем клиентам кроме того у которого ID=5. Изменения сервера передаются всем. Это все конечно если хочется поддержать одну и ту же структуру на клиенте и сервере...

ID клиента должен генерить сервер при подключении клиента - они только на сервере и хранятся...


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 21, 2010, 13:09
Хм.  ::)
Тогда можно сделать, как ты и пробовал.
Сервер открывает два порта (например 6177 и 6178). Все кто подключаются к первому порту - отправляют данные (Sender), все кто подключаются ко второму - получают результат (Recv).

Воспользовался советом. Спасибо. Работает :)

Что-то судари вы заморачиваетесь. Посмотрите внимательно Fortune Client & Fortune Server в демках. там даже не создается новый класс наследуемый от QTcpSocket, все можно сделать стандартный сервер. смотрим например так.

Этот способ мне тоже понравился... но у меня почему-то программа аварийно завершается. Перерыл эти примеры fortuneClient и fortuneServer. Сделать так же, может что-то упустил. Как подсказал BRE конечно все работает, но пришлось делать два класса Sender и Reciever наследуемых от QtcpServer и открывать два порта. А тут и кода и меньше и работает один порт, но прога аварийно завершается, хгтя компилятор ничего не сообщает. Идет возврат кода -1073741795.
Вот код widget.h:
Код
C++ (Qt)
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QTcpServer>
#include <QTcpSocket>
 
class QTcpServer;
 
class Server : public QWidget
{
   Q_OBJECT
public:
   Server(QWidget *parent = 0);
   int i;
   QTcpSocket *socket1, *socket2;
   QString strA, buf;
public slots:
  void readPack();
  void readClient();
protected:
   void incomingConnection();
};
#endif // WIDGET_H
 
widget.cpp:
Код
C++ (Qt)
#include "widget.h"
#include <QMessageBox>
QTcpServer *server = new QTcpServer;
 
Server::Server(QWidget *parent)
   :QWidget(parent)
{
   connect(socket1, SIGNAL(readyRead()), this, SLOT(readPack()));
   connect(socket2, SIGNAL(readyRead()), this, SLOT(readClient()));
   i = 0;
   server->setParent(this);
   if (!server->listen(QHostAddress::Any, 6178))
   {
       QMessageBox::critical(NULL, "Error", "Невозможно открыть порт 6178");
       server->close();
       return;
   }
}
 
void Server::incomingConnection()
{
    ++i;
    if (i == 2)
    {
        socket1 = server->nextPendingConnection();
        socket2 = server->nextPendingConnection();
    }
}
void Server::readClient()
{
    QDataStream in(socket2);
    QDataStream out(socket1);
    in >> strA;
    if (strA == "Hello")
        buf = "Yeah";
    else
        buf = "Nothing";
    out << buf;
}
void Server::readPack()
{
    QDataStream in(socket1);
    QDataStream out(socket2);
    in >> strA;
    out << strA;
}
 
 
main.cpp:
Код
C++ (Qt)
#include <QTextCodec>
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QTextCodec *codec = QTextCodec::codecForName("cp1251");
   QTextCodec::setCodecForLocale(codec);
   QTextCodec::setCodecForTr(codec);
   QTextCodec::setCodecForCStrings(codec);
       Server tcpServer;
       tcpServer.setGeometry(0, 0, 0, 0);
       tcpServer.show();;
return a.exec();
}
 

Ткните пальцем в ошибку пожалуйста  ;D


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: break от Февраль 21, 2010, 13:41
Ты в конструкторе пытаешься коннектить еще не созданные объекты socket1 и socket 2

Стоит пользоваться отладчиком

так работает - точнее не знаю что там работает - программа запускается и не падает...
Код
C++ (Qt)
Server::Server(QWidget *parent)
:QWidget(parent)
{
///////////////////////////////////////////////////////////////////////
socket1 = new QTcpSocket();
socket2 = new QTcpSocket();
///////////////////////////////////////////////////////////////////////
 
connect(socket1, SIGNAL(readyRead()), this, SLOT(readPack()));
connect(socket2, SIGNAL(readyRead()), this, SLOT(readClient()));
i = 0;
server->setParent(this);
if (!server->listen(QHostAddress::Any, 6178))
{
QMessageBox::critical(NULL, "Error", "Невозможно открыть порт 6178");
server->close();
return;
}
}


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 21, 2010, 14:11
Ты в конструкторе пытаешься коннектить еще не созданные объекты socket1 и socket 2

Пасибо  :) Терь буду правильно устанавливать контакт...

Всем спасибо!


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 21, 2010, 18:18
Вот я немного изменил, но почему-то пакеты не доходят. Подскажите что забыл добавить?
main.cpp
Код
C++ (Qt)
#include <QTextCodec>
#include <QtGui/QApplication>
#include "widget.h"
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QTextCodec *codec = QTextCodec::codecForName("cp1251");
   QTextCodec::setCodecForLocale(codec);
   QTextCodec::setCodecForTr(codec);
   QTextCodec::setCodecForCStrings(codec);
       Server tcpServer;
       tcpServer.setGeometry(0, 0, 0, 0);
       tcpServer.show();;
return a.exec();
}
 

widget.h
Код
C++ (Qt)
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtGui/QWidget>
#include <QTcpServer>
#include <QTcpSocket>
 
class QTcpServer;
 
class Server : public QWidget
{
   Q_OBJECT
public:
   Server(QWidget *parent = 0);
   int i;
   QString strA;
   QTcpSocket *socket1, *socket2;
public slots:
  void readPack();
  void readClient();
protected:
   void incomingConnection();
};
#endif // WIDGET_H
 

widget.cpp
Код
C++ (Qt)
#include "widget.h"
#include <QMessageBox>
QTcpServer *server = new QTcpServer;
Server::Server(QWidget *parent)
   :QWidget(parent)
{
   socket1 = new QTcpSocket();
   socket2 = new QTcpSocket();
   connect(socket1, SIGNAL(readyRead()), this, SLOT(readPack()));
   connect(socket2, SIGNAL(readyRead()), this, SLOT(readClient()));
   connect(socket1, SIGNAL(disconnected()), this, SLOT(deleteLater()));
   connect(socket2, SIGNAL(disconnected()), this, SLOT(deleteLater()));
   i = 0;
   server->setParent(this);
   if (!server->listen(QHostAddress::Any, 6178))
   {
       QMessageBox::critical(NULL, "Error", "Невозможно открыть порт 6178");
       server->close();
       return;
   }
}
 
void Server::incomingConnection()
{
    ++i;
    if (i == 2)
    {
        socket1 = server->nextPendingConnection();
        socket2 = server->nextPendingConnection();
    }
}
 
void Server::readClient()
{
    QDataStream in(socket2);
    QDataStream out(socket1);
    in.setVersion(QDataStream::Qt_4_6);
    in >> strA;;
    out << strA;
}
 
void Server::readPack()
{
    QDataStream recv(socket1);
    QDataStream send(socket2);
    recv.setVersion(QDataStream::Qt_4_6);
    recv >> strA;
    send << strA;
}
 
 


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: SABROG от Февраль 23, 2010, 18:48
Код
C++ (Qt)
   connect(socket1, SIGNAL(readyRead()), this, SLOT(readPack()));
   connect(socket2, SIGNAL(readyRead()), this, SLOT(readClient()));
 

Вероятно потому, что readyRead() никогда не приходит, так как ни одному из сокетов не хватает смелости завести разговор (отправить данные).


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 23, 2010, 20:57
Код
C++ (Qt)
   connect(socket1, SIGNAL(readyRead()), this, SLOT(readPack()));
   connect(socket2, SIGNAL(readyRead()), this, SLOT(readClient()));
 
Вероятно потому, что readyRead() никогда не приходит, так как ни одному из сокетов не хватает смелости завести разговор (отправить данные).

Почему не приходит? Я с клиента посылаю данные на socket2. Значит должен сработать readClient(), и отправиться строка на socket1. Там (на другом уже клиенте) обрабатывается и посылается ответ. Этот ответ приходит на socket1 readPack() и посылается на socket2 (моему первоначальному клиенту).

 ???


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: SABROG от Февраль 24, 2010, 03:13
Чего-то у тебя incomingConnection() нигде не коннектится,  даже слотом не является, непонятно как ты подключения новые обрабатываешь.


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 24, 2010, 11:35
Чего-то у тебя incomingConnection() нигде не коннектится,  даже слотом не является, непонятно как ты подключения новые обрабатываешь.

Хм... protected: void incomingConnection()  :)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: kuzulis от Февраль 24, 2010, 12:46
G-virus , что сие вообще значит???

1.
Код:
...
class Server : public QWidget
....
...

2.
Код:
...
QTcpServer *server = new QTcpServer;
Server::Server(QWidget *parent)
    :QWidget(parent)
...
...

o_O ...  что за бредятина (ИМХО) ?

Цитировать
Хм... protected: void incomingConnection()  Улыбающийся

и что толку от этого? а? с чего вдруг твой ВИДЖЕТ Server будет обрабатывать:  void Server::incomingConnection() ?


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 24, 2010, 13:24
Возможно я не так понял. Но мне предлагали такой вариант. и в fortuneServer и fortuneClient что-то похожее.

Я сделал наследование от QTcpServer и немного изменил код. Но теперь у меня не получается получить доступ к объекту Server, чтобы выполнить манипуляции в incomingConnection().


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: kuzulis от Февраль 24, 2010, 13:36
G-virus ,

Повнимательнее разбери примеры: fortuneserver, threadedfortuneserver и т.п.
и  сравни с тем как сделано у тебя! Найди отличия! :)


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 24, 2010, 13:37
Дело в том, что отличий куча  ;D вообще не похоже как у меня. Я конечно могу скопировать и использовать fortuneServer или threadedfortuneServer, но хотелось бы свой серв довести до ума.


Название: Re: Сервер, обслуживающий два клиента, но без QThread
Отправлено: G-virus от Февраль 28, 2010, 18:39
Короче ни черта я не понял всякие fortuneClient и fortuneServer (threadedfortuneServer)  ;D Воспользовался советом BRE