Ошибка при использовании QTcpSocket

Я создаю (очень простой) сервер MJPG для отображения данных веб-камеры в браузере. Мне пока частично это удается.

Вот мой код:

TcpServer::TcpServer(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
// whenever a user connects, it will emit signal
connect(server, SIGNAL(newConnection()),
    this, SLOT(newConnection()));
if (!server->listen(QHostAddress::Any, 9999))
    qDebug() << "Server could not start";
else
    qDebug() << "Server started!";
}
...
void TcpServer::newConnection()
{
QTcpSocket *socket = server->nextPendingConnection();
QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
    "Cache-Control: no-cache\r\n" \
    "Cache-Control: private\r\n" \
    "Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n");
socket->write(ContentType);
std::vector<uchar> buff;
Mat img; //OpenCV Material
while (1) {
    // Image to Byte Array via OPENCV Method
    buff.clear();buff.empty();
    vCapture->read(img); //Read from webcam

    imencode(".jpg", img, buff, compression_params);
    std::string content(buff.begin(), buff.end());
    QByteArray CurrentImg(QByteArray::fromStdString(content));
    QByteArray BoundaryString = ("--boundary\r\n" \
        "Content-Type: image/jpeg\r\n" \
        "Content-Length: ");
    BoundaryString.append(QString::number(CurrentImg.length()));
    BoundaryString.append("\r\n\r\n");

    socket->write(BoundaryString);
    socket->write(CurrentImg); // Write The Encoded Image
    socket->flush();
}

}

Проблема —

Когда я запускаю эту программу, отображается первое изображение. После этого в приложении постоянно печатается следующая ошибка:

QIODevice::write (QTcpSocket): device not open

Было похоже, что сокет закрылся, поэтому я использовал повторную инициализацию сокета, вот так - socket = server->nextPendingConnection();, хотя приложение выдало ошибку с этим кодом. Любая помощь, как это исправить?

ИЗМЕНИТЬ –

Я попробовал метод лямбда, и он работал нормально. Однако я все еще сталкиваюсь с двумя проблемами:

  1. Размер изображения должен быть слишком маленьким (около 270x480 с самым низким качеством JPG).

  2. (БОЛЬШЕ ВАЖНО) Мне приходится вручную нажимать кнопку перезагрузки в браузере, чтобы перезагрузить изображение, оно не меняется автоматически с одного изображения на другое.


person FadedCoder    schedule 04.05.2016    source источник
comment
Давно не использовал QT, поскольку проект, на котором вы основываете свой код, но я рекомендую вам использовать задержку между отправкой кадров (например, QThread::usleep(500);). Основная причина заключается в том, что этот метод (если говорить о сервере MJPEG в настоящее время не очень эффективен, когда речь идет о производительности. В моих тестах, когда я устанавливал задержку между отправкой кадров на низкое значение, видео всегда зависало в какой-то момент.   -  person hypnomaki    schedule 04.05.2016


Ответы (2)


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

void MjpegStreamingEngine::StartServer(){

    m_TcpHttpServer = new QTcpServer();

    m_TcpHttpServer->connect(m_TcpHttpServer,
                             SIGNAL(newConnection()),
                             this,
                             SLOT(TcpHttpconnected()));

    m_TcpHttpServer->listen(QHostAddress::Any,
                            8889);
}

void MjpegStreamingEngine::TcpHttpconnected(){

    m_TcpHttpClient = m_TcpHttpServer->nextPendingConnection();

    m_TcpHttpClient->connect(m_TcpHttpClient,
                             SIGNAL(readyRead()),
                             this,
                             SLOT(TcpHttpreadyRead()));

}

void MjpegStreamingEngine::TcpHttpreadyRead(){

    m_TcpHttpClient->readAll(); // Discard "Get Request String"

    QByteArray ContentType = ("HTTP/1.0 200 OK\r\n" \
                              "Server: en.code-bude.net example server\r\n" \
                              "Cache-Control: no-cache\r\n" \
                              "Cache-Control: private\r\n" \
                              "Content-Type: multipart/x-mixed-replace;boundary=--boundary\r\n\r\n");

    m_TcpHttpClient->write(ContentType);


    while(1){

        if (m_TcpHttpClient->isOpen())
        {

            // Send Image

            QThread::msleep(10);

        }else{
            return;
        }

    }

    m_TcpHttpClient->disconnect(); //Should never be Reached
}
person hypnomaki    schedule 11.05.2016

Похоже сокет закрылся

Вместо того, чтобы гадать, подключитесь к сигналам ошибок TCPServer и TCPSocket, чтобы узнать, когда возникают ошибки или когда клиент отключается.

У вас возникла проблема с циклом while(1). Qt — это управляемый событиями фреймворк, поэтому наличие кода в бесконечном цикле на главном поток будет предотвращать доставку событий.

Вместо бесконечного цикла подключитесь к сигналу QTcpSocket::readyRead и обрабатывать данные при вызове подключенного слота.

Qt демонстрирует это на примере Fortune Server и Fortune Client.

Если вы используете C++ 11, вы можете использовать соединение с лямбда-функцией для обработки readyRead, например так

void TcpServer::newConnection()
{
    ...

    QTcpSocket *m_TcpHttpClient = server->nextPendingConnection();
    connect(m_TcpHttpClient, &QTcpSocket::readyRead, [=](){

        // handle received data here

    });

}
person TheDarkKnight    schedule 04.05.2016
comment
Я попробовал ваш ответ, но теперь я получаю эту ошибку - Unhandled exception at 0x000000C039E24388 (ucrtbase.dll) in app.exe: An invalid parameter was passed to a function that considers invalid parameters fatal. - person FadedCoder; 04.05.2016
comment
Пожалуйста, обновите свой вопрос, чтобы показать код, который вызывает ошибку. - person TheDarkKnight; 04.05.2016
comment
Я вижу обновление, но код все тот же. Чтобы помочь вам с этой ошибкой, нам нужно увидеть код, который ее создает. - person TheDarkKnight; 05.05.2016
comment
Этот код отправляет ContentType клиенту каждый раз, когда сервер получает данные - я ожидаю, что это не то, что вам нужно, поскольку вы не делаете этого в исходном коде. Затем я предлагаю закомментировать код openCV и просто отправить простую строку между клиентом и сервером. Если это удалось, проблема заключается в обработке изображения, а не в подключении к сокету. - person TheDarkKnight; 05.05.2016
comment
Удалено предыдущее редактирование, поскольку оно было исправлено с помощью кода, предоставленного TheDarkKnight. Ваше редактирование изменяет вопрос, что не рекомендуется для SO. Я предлагаю принять этот вопрос как ответ (выбрав галочку) и начать новый вопрос SO, что сделает его более заметным для сообщества и с большей вероятностью найдет приемлемые ответы. - person TheDarkKnight; 06.05.2016