Повышение async_read не дает мне флаг «конец кадра» - PullRequest
1 голос
/ 16 февраля 2012

Я все еще работаю над каким-то клиентом для связи с IP-камерой. Теперь у меня есть следующая проблема:

Я отправляю запрос на камеру (в частности RTSP DESCRIBE). Теперь я получаю ответ, который выглядит так:

RTSP/1.0 200 OK
CSeq: 2
Date: Thu, Jan 01 1970 00:31:41 GMT
Content-Base: rtsp://192.168.0.42/mpeg4?mode=Live&stream=-1&buffer=0&seek=0&fps=100&    metainfo=/
Content-Type: application/sdp
Content-Length: 517

Это заголовок ответа, за которым следует так называемый Session Description, размер которого указан в поле Content-Length. На самом деле меня не волнует Session Description, меня просто интересует поле Content-Base. Но, тем не менее, поскольку в одном и том же сокете есть связь, мне нужно избавиться от всех данных.

Для приема я использую async_read звонки с boost::asio. Мой код выглядит (упрощенно) так:

CommandReadBuffer::CallbackFromAsyncWrite()
{
boost::asio::async_read_until(*m_Socket, m_ReceiveBuffer,"\r\n\r\n",
            boost::bind(&CommandReadBuffer::handle_rtsp_describe, this->shared_from_this(),
                    boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}

Этот читает по крайней мере заголовок (показанный выше), так как он заканчивается пустой строкой. Как обычно для async_write, он просто читает некоторые данные, но не имеет значения. Теперь к следующей функции обратного вызова:

void CommandReadBuffer::handle_rtsp_describe(const boost::system::error_code& err,size_t bytesTransferred)
{
std::istream response_stream(&m_ReceiveBuffer);
std::string header;
// Just dump the data on the console 
while (std::getline(response_stream, header))
{
   // Normally I would search here for the desired content-base field
   std::cout << header << "\n";
}
 boost::asio::async_read(*m_Socket, m_ReceiveBuffer, boost::asio::transfer_at_least(1),
         boost::bind(&CommandReadBuffer::handle_rtsp_setup, this->shared_from_this(),
                            boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

Теперь это тоже хорошо работает, если я распечатываю количество полученных байтов, это всегда 215.

Теперь перейдем к критическому обратному вызову:

void CommandReadBuffer::handle_rtsp_setup(const boost::system::error_code& err, size_t bytesTransferred)
{
std::cout << "Error: " << err.message() << "\n";
    if (!err)
    {
      // Write all of the data that has been read so far.
      std::cout << &m_ReceiveBuffer;

    // Continue reading remaining data until EOF.
    m_DeadlineTimer->async_wait(boost::bind(&CommandReadBuffer::handleTimeout, this->shared_from_this(),boost::asio::placeholders::error));
      boost::asio::async_read(*m_Socket, m_ReceiveBuffer, boost::asio::transfer_at_least(1),
             boost::bind(&CommandReadBuffer::handle_rtsp_setup, this->shared_from_this(),
                                boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
else if (err != boost::asio::error::eof)
{
  std::cout << "Error: " << err.message() << "\n";
}
else
{
    std::cout << "End of Frame " << err.message() << "\n";
}
}

Эта часть читает 220 байтов. Если я посмотрю на консольный вывод этого вызова и сравню его с фактической полезной нагрузкой кадра (как видно из Wireshark), то увижу, что все данные получены. Теперь я действительно предположил бы, что async_read установит мне ошибку eof. Но вместо этого код возврата error равен success, поэтому он снова вызывает async_read. На этот раз нет данных, которые должны быть получены, и он никогда не вызывает функцию обратного вызова (так как больше не будет входящих данных).

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

Теперь это очень похоже на реализацию примера Boost для асинхронного HTTP-клиента. То же самое делается в HTTP-клиенте Example Boost Async . Я реализовал это в другом вызове, и там это на самом деле работает.

Теперь, на мой взгляд, это не должно иметь никакого значения для вызова async_read, если это HTTP или RTSP - конец кадра - это конец кадра, если больше нет данных для чтения.

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

void async_read(
    AsyncReadStream & s,
    basic_streambuf< Allocator > & b,
    CompletionCondition completion_condition,
    ReadHandler handler);

, что означает, что функция будет продолжаться до

The supplied buffer is full (that is, it has reached maximum size).
The completion_condition function object returns 0.

Так что, если больше нет данных для чтения, они просто продолжаются. Но я также попробовал перегруженную функцию без параметра CompletionCondition, который должен возвращаться при возникновении ошибки (EOF !!!) - но это просто не вызовет обратного вызова ...

Есть предложения? Я просто не понимаю, что я делаю неправильно ...

1 Ответ

3 голосов
/ 20 февраля 2012

Я написал клиентскую и серверную библиотеку RTSP с использованием boost asio и могу предложить следующий совет: Синтаксис сообщения RTSP является общим: нет необходимости в разных обработчиках DESCRIBE и SETUP.В общем случае

  • написать запрос RTSP
  • , чтобы прочитать ответ, выполнить boost::asio::async_read_until("\r\n\r\n")
  • , а затем проверить заголовок Content-Length
  • если content_length> 0 делает boost::asio::transfer_at_least(content_length)

Далее, почему вы ожидаете EOF?Соединение все еще открыто: сервер ожидает либо другой SETUP, либо запрос PLAY и обычно не закрывает соединение, пока не истечет тайм-аут соединения RTSP TCP, значение которого по умолчанию равно 60 секундам согласно RFC2326..

Если в вашем приложении вы завершили взаимодействие с сервером RTSP, закройте соединение после прочтения ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...