Я все еще работаю над каким-то клиентом для связи с 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
!!!) - но это просто не вызовет обратного вызова ...
Есть предложения? Я просто не понимаю, что я делаю неправильно ...