Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Получение данных на сервере?
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Ввод/Вывод, Сеть. Межпроцессное взаимодействие
V7T
Здравствуйте, пишу клиент/серверное приложение, я использую FortuneClient и ThreadedFortuneServer из SDK QT.

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

Собственно вот часть кода клиента:
void Client::requestNewFortune()
{
    //getFortuneButton->setEnabled(false);
    blockSize = 0;
    tcpSocket->abort();

    tcpSocket->connectToHost(hostLineEdit->text(),
                             portLineEdit->text().toInt());

    QPixmap screenshot = QPixmap::grabWindow(QApplication::desktop()->winId());
    QByteArray foto;
    QBuffer buffer(&foto);
    buffer.open(QIODevice::WriteOnly);
    screenshot.save(&buffer,"jpg");

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0;
    out<<buffer.data();//отправяем данные скриншота
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

    qDebug()<<"Size of buffer: "<<buffer.size();//число...
    qDebug()<<"Size of block: "<<block.size();//число...
    
    qDebug()<< tcpSocket->write(block);

    tcpSocket->disconnectFromHost();
    tcpSocket->waitForDisconnected();



часть кода сервера:
void FortuneThread::run()
{
    QTcpSocket tcpSocket;
    if (!tcpSocket.setSocketDescriptor(socketDescriptor))//работаем с дескриптором
    {
        emit error(tcpSocket.error());
        return;
    }


    QDataStream in(&tcpSocket);//здесь без взятия адреса ругается
    in.setVersion(QDataStream::Qt_4_0);

    QByteArray buffer;
    in >> buffer;

    qDebug()<<buffer.size();
    qDebug()<<tcpSocket.bytesAvailable();

    tcpSocket.disconnectFromHost();
    //tcpSocket.waitForDisconnected();
}


В коде сервера явно не все впорядке (возможно и клиенте) :) , и Вас уважаемые гуру наверно :bad: .

Правильно ли данные отправляются?
Может стоит создать connect(сокет,сигнал(readyRead()),...,slot(..)), впринципе, что я только не делал, все одно - ничего)).
Не первый день пытаюсь разобраться, окончательно запутался.

Подскажите, пожалуйста.
silver47
ThreadedFortuneServer устроен таким образом, что при новом соединении создается новый поток, а в нем, в свою очеред создается новый сокет. В сокет вываливается некая фраза, затем сокет на серверной стороне завершается и поток прибивается.

Обычно же, работа клиент-серверного ПО выглядит так:
- клиент подключается к серверу
- создается новый поток
- создается сокет
- клиент отправляет запрос
- сервер принимет запрос (сигнал ReadyRead() сокета)
- сервер отвечает клиенту
- клиент принимает ответ (сигнал ReadyRead() сокета клиента)
- клиент отключается
- уничтожается сокет
- уничтожается поток (сигнал finished() и слот DeleteLater() потока)

P.S. где-то уже не так давно выкладывал я для кого-то пример.
Нашел. В примере создается сервер, который вываливает в qDebug все что получает. При дисконекте клиента, сокет и поток удаляются
V7T
Silver, спасибо за отзыв.

Я в принципе сам не много разобрался с получением данных на сервере, но пока без многопоточной реализации. Решил ее пока не трогать. Теперь появилась другая проблема, я написал небольшой класс для клиента, вызываю его в Main и все работает на ура (скриншот отправляется и принимается на сервере), но стоит в классе, который работает с формой (MainWindow.cpp) загнать тот же код, что и в Main, в обработчик события кнопки, как ни какой отправки не наблюдается, но соединение происходит и сервер его отлавливает (если в дебагер вывести содержимое сокета, то будет по нулям). Еще заметил, что если в Main несколько раз запустить один и тот же кусок кода, то первое соединение работает, а последующие нет (соединение происходит, но ни чего не отправляется). Причем, если через консоль запустить несколько экземпляров программы, то все работает, т.е. сервер не причем

Вот привожу код клиента:
/////MAIN.cpp
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    QPixmap pixmap=QPixmap::grabWindow(QApplication::desktop()->winId());

    QDateTime time=QDateTime::currentDateTime();
    QString StringTime = time.toString("dd.MM.yyyy hh:mm:ss");

    QString address ="127.0.0.1";
    int port = 7777;

    SendImage s(address, port);
    // To send an image give the image and the image name
    s.send(pixmap.toImage(), StringTime+".png");

    return a.exec();
}


/////////MainWindow.cpp
#include <mainwindow.h>
#include <ui_mainwindow.h>
#include <sendimage.h>
#include <QtGui>

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::doSendImage()
{
    QDateTime time=QDateTime::currentDateTime();
    QString StringTime = time.toString("dd.MM.yyyy hh:mm:ss");

    QPixmap pixmap=QPixmap::grabWindow(QApplication::desktop()->winId());

    QString address = ui->hostLineEdit->text();
    int port = ui->portLineEdit->text().toInt();

//.............................................
//вызываю экземпляр
    SendImage s(address, port);
    // To send an image give the image and the image name
    s.send(pixmap.toImage(), StringTime+".png");
//.............................................
}

void MainWindow::on_startSendImage_clicked()
{
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(doSendImage()));
    timer->start(5000);
}

void MainWindow::on_stopSendImage_clicked()
{
    //global timer...
    //timer->stop..
}


[b]/////////Как работает класс[/b]
SendImage::SendImage(QString address, int port)
{
    this->address = address;
    this->port = port;

    socket = new QTcpSocket(this);

    connect(socket, SIGNAL(connected()), SLOT(setConnected()));
}

void SendImage::setConnected()
{
        reallySend(this->name, this->image);
}

void SendImage::send(QImage image, QString name)
{
    // Store the data
    this->name = name;
    this->image = image;
    // Start connection
    toggleConnection();
}

void SendImage::reallySend(QString name, /*QPixmap*/QImage image)
{
        if(socket->state() == QAbstractSocket::ConnectedState)
        {
            image.setText("name", name);
            QByteArray ba;
            QBuffer buffer(&ba);
            image.save(&buffer, "PNG");

            QByteArray block;
            QDataStream out(&block, QIODevice::WriteOnly);
            out.setVersion(QDataStream::Qt_4_0);
            out << (quint16)0;
            out << ba;
            out.device()->seek(0);
            out << (quint16)(block.size() - sizeof(quint16));

            socket->write(block);
            qDebug()<<"Size buffer:"<<block.size();
            qDebug()<<"Real Size Screen:"<<ba.size();
        }
        else
        {
            qDebug("Image has not been send. No connection established!");
        }
        toggleConnection();
}

void SendImage::toggleConnection()
{
        if (socket->state() == QAbstractSocket::UnconnectedState)
        {
            socket->connectToHost(this->address, this->port, QIODevice::WriteOnly);
        }
        else
        {
            socket->disconnectFromHost();
        }
}


[b]//////Код класса[/b]
class SendImage : public QObject
{
        Q_OBJECT

public:
        SendImage(QString address, int port);
        void send(QImage image, QString name);

private slots:
        void setConnected();

private:
        void toggleConnection();
        void reallySend(QString name, QImage image);

public:
        QString name;
        QImage image;
        QString    address;
        int port;
        QTcpSocket* socket;
};


Код сервера
///Собственно обработка на сервере, мало ли, может я что-то делаю не так

ImageServer::ImageServer(QObject* parent) : QTcpServer(parent)
{
    connect(this, SIGNAL(newConnection()), this, SLOT(addConnection()));
}

void ImageServer::addConnection()
{
    qDebug("Adding connection");
    QTcpSocket* connection = nextPendingConnection();
    //connections in imageserver.h (QList<QTcpSocket>)
    connections.append(connection);
    connect(connection, SIGNAL(disconnected()), SLOT(removeConnection()));
}

void ImageServer::removeConnection()
{
    receiveImage();
    QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
    connections.removeAll(socket);
    socket->deleteLater();
}

void ImageServer::receiveImage()
{
    QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
    quint16 blockSize=0;

    QDataStream in(socket);
    in.setVersion(QDataStream::Qt_4_0);

    //qDebug()<<socket->size();
    //qDebug()<<socket->bytesAvailable();
    if (blockSize == 0)
    {
        if (socket->bytesAvailable() < (int)sizeof(quint16))
        {
            return;
        }
        in >> blockSize;
    }
    if (socket->bytesAvailable() < blockSize)
    {
        return;
    }

    QByteArray Block;
    in >> Block;
    //qDebug()<<socket->size();
    //qDebug()<<socket->bytesAvailable();

    qDebug()<<blockSize;
    qDebug()<<Block.size();

    QImage image;
    image.loadFromData(Block,"PNG");
    QString filename = image.text("name");
    image.save(QString(getenv("HOME"))+"/Screenshot/"+filename,"PNG",100);

}


Что не так, подскажите
silver47
А класс SendImage обязательно должен быть таким запутанным. Может просто послать эту картинку и всего делов?
Кроме того, насколько мне известно, кажется, что, оператор << перегружен для QImage. Если это так, то в этом шаманстве:
QByteArray ba;
QBuffer buffer(&ba);
image.save(&buffer, "PNG");
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << ba;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
нет смысла. Достаточно напрямую записать в поток и все:
ByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_7);
out << quint16(0) << QImage;
out.device()->seek(0);
out << quint16(block.size() - sizeof(quint16));
socket->write(block);


Пы.Сы. А Вы уверенны, что картинка будет точно размером меньше 65килобайт?
AntonT
Цитата(silver47 @ 24.10.2011, 9:55) *
Пы.Сы. А Вы уверенны, что картинка будет точно размером меньше 65килобайт?


извиняюсь что влезаю со своим вопросом, а можно чуть по подробней ?

пытаюсь по сети передать сообщения, маленькие проходят а побольше нет....
silver47
Цитата
извиняюсь что влезаю со своим вопросом, а можно чуть по подробней ?

Сообщение передается блоками данных. До того как сервер начнет что-либо читать из сокета, он должен знать точно размер блока, который читает. Для этого, в самом начале блока данных передается размер этого блока. За это отвечает вот это:
out << quint16(0)
. В данном случае размер блока не сможет превышать размер двухбайтового беззнакового целого, т.е. 65535 байт.
V7T
Цитата(silver47 @ 24.10.2011, 10:55) *
А класс SendImage обязательно должен быть таким запутанным. Может просто послать эту картинку и всего делов?

Пы.Сы. А Вы уверенны, что картинка будет точно размером меньше 65килобайт?



...хм, например, в дебагер размер Image вывожу, получаю около 200000 (размер байтового массива QByteArray), т.е., не хочу сморозить глупость, но как я понимаю это 200000 байт, следовательно изображение занимает около 200 кбайт. На сервере я спокойно их получаю. Получается, что все сообщение, в данном случае, разобьется на 4 блока.

...кстати да, убрал полностью класс SendImage, решил не заморачиваться с ним.

Что касается прямого запихивания QImage в QDataStream,
out<<image (альтернатива out<<(&out,&image) как то так, ) запихивается на ура, но на сервере QDataStream взяв сокет не хочет выводить данные в image напрямую (ошибок не наблюдается), т.е. сокет не пустой (сколько отправлено, столько и пришло).

все как в документации.
QDataStream &    operator<< ( QDataStream & stream, const QImage & image )
QDataStream &    operator>> ( QDataStream & stream, QImage & image )


А теперь самое интересное, неоднократно натыкался в интернете, на одну особенность или может быть в силу таких же кривых рук.
Если передавать изображение так:
    QByteArray ba;
    QBuffer buffer(&ba);
    image.save(&buffer,"PNG");

т.е. сохранить изображение в массив байтовый в формате PNG, на сервере тоже принимать в PNG (image.loadFromData(Block,"PNG");, то все работает замечательно. Но стоит поменять формат загрузки/разгрузки в/из байтового массива, например JPG, BMP то при получении на сервере (in>>Block, image.loadFromData(Block,"...")) c последующей проверкой image.isNull() - постое, по нулям, хотя поток блоковый заполнен. В результате, если отправлять/принимать только в PNG, то сохранять придется тоже в PNG (image.save(..."PNG")), в другом формате не получается, следовательно изображение будет занимать 3 метра. А это не выгодно, хотя мне для учебы не принципиально.

Решение есть, если я отправляю просто pixmap, перед этим загнанный в массив, на сервере потом могу сохранять в любом формате, при этом экономя место.





silver47
Цитата(V7T @ 25.10.2011, 3:13) *
Получается, что все сообщение, в данном случае, разобьется на 4 блока.

Никто за Вас ничего разбивать не будет. Для проверки просто выведите значение block.size()

Цитата(V7T @ 25.10.2011, 3:13) *
Что касается прямого запихивания QImage в QDataStream,

Начните с малого. Передайте какие-нибудь числа, потом строки, потом байтовый масив произвольной длинны. Как только получиться передавать байтовый массив произвольной длины - Вы станете ближе к решению Вашей задачи.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.