Boost async_read 不会给我一个“帧结束”的信息。旗帜

发布于 2025-01-06 06:04:33 字数 3974 浏览 1 评论 0原文

我仍在开发某种与 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-Base 字段感兴趣。但是,由于同一套接字上有一些通信,我需要删除所有数据。

为了接收,我使用来自 boost::asioasync_read 调用。 我的代码看起来(简化)如下:

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。这次没有数据被接收,并且它永远不会调用回调函数(因为不会再有传入的数据)。

现在我实际上不知道如何确定所有数据都已发送。实际上我希望设置错误标志。

现在,这与异步 HTTP 客户端的 Boost 示例的实现非常相似。在 Boost 异步 HTTP 客户端示例。我在另一个电话中实现了这一点,并且它确实有效。

现在,我认为,无论是 HTTP 还是 RTSP,对于 async_read 调用都没有什么区别 - 如果没有更多数据可供读取,则帧结束就是帧结束。

我还知道根据我正在使用的 boost 文档,

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

这意味着该功能将持续到

提供的缓冲区已满(即已达到最大大小)。
completion_condition 函数对象返回 0。

因此,如果没有更多数据可供读取,它就会继续。 但我也尝试了没有 CompletionCondition 参数的重载函数,该函数应该在发生错误时返回( EOF !!!) - 但这也不会回调.. 有什么建议吗

?我只是不明白我做错了什么......

I'm still working on some kind of client for communication with an IP Camera. Now I have the following issue:

I send a request to the camera ( a RTSP DESCRIBEin particular ). Now I get it's answer which looks like this:

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

This is the header of the answer, followed by a so called Session Description which has the size shown in the field Content-Length. Actually I don't care much for the Session Description , I'm just interested in the Content-Base field. But still, since there is some communication following on the same socket, I need to get rid of all the data.

For receiving'm using the async_read calls from boost::asio.
My code looks ( simplified ) like this:

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));
}

This one reads at least the header ( shown above ) since its terminated by a blank line. As usual for async_write it just reads some more of the data, but nevermind. Now to the next callback function:

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));
}

Now this works fine as well, if I print out the number of received bytes it's always 215.

Now we go on to the critical callback:

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";
}
}

This part reads 220 Bytes. If I look at console Output from this call and compare it with the actualy payload of the frame ( as seen in Wireshark ) I can see that all data has been received. Now I would actually assume that async_read would set me the eof error. But instead the return code of error is success and so it calls async_read again. This time there is no data to be received and it never calls the callback function ( since there will be no more incoming data ).

Now I actually don't know how I could determine that all data has been sent. Actually I would expect the error flag to be set.

Now this is very similar to the implementation of the Boost Example for an Async HTTP client. Also it is done the same way in the Example Boost Async HTTP Client. I implemented this in another call and there it actually works.

Now in my opinion it should make no difference for the async_read call wether it is HTTP or RTSP - end of frame is end of frame, if there is no more data to read.

I'm also aware that according to the boost documentation I am using

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

which means the function will continue until

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

So if there is no more data to read, it just continues.
But I also tried the overloaded function without the CompletionCondition parameter, which should return when an error occurs ( EOF !!! ) - But this just won't callback either...

Any suggestions? I just don't get what I'm doing wrong...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

獨角戲 2025-01-13 06:04:33

我使用 boost asio 编写了一个 RTSP 客户端和服务器库,可以提供以下建议:
RTSP 消息语法是通用的:不需要不同的 DESCRIBE 和 SETUP 处理程序。一般来说,

  • 编写一个 RTSP 请求
  • 来读取响应,执行 boost::asio::async_read_until("\r\n\r\n")
  • 然后检查 Content-Length header
  • 如果 content_length > 0 执行 boost::asio::transfer_at_least(content_length)

此外,为什么您期望 EOF?连接仍然打开:服务器正在等待另一个 SETUP 或 PLAY 请求,并且通常不会关闭连接,直到 RTSP TCP 连接超时,根据 RFC2326

如果在您的应用程序中,您已完成与 RTSP 服务器的交互,请在读取响应后关闭连接。

I have written an RTSP client and server library using boost asio and can offer the following advice:
The RTSP message syntax is generic: there is no need for different DESCRIBE and SETUP handlers. In general

  • write an RTSP request
  • to read the response do a boost::asio::async_read_until("\r\n\r\n")
  • then check for the Content-Length header
  • if content_length > 0 do a boost::asio::transfer_at_least(content_length)

Further, why are you expecting an EOF? The connection is still open: the server is waiting for either another SETUP or a PLAY request and typically won't close the connection until the RTSP TCP connection has been timed out, which has a default value of 60 seconds according to RFC2326.

If in your application, you have completed interaction with the RTSP server, close the connection after you have read the response.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文