通过boost :: asio :: udp以高数据速率发送图像数据?

通过boost :: asio :: udp以高数据速率发送图像数据?

问题描述:

我有4MB的图像,必须通过以太网发送。图像来自一台计算机,X86_64,它生成它们,并发送到另一台计算机,ARM。图像以两位数帧速率生成,我使用cat 6a电缆确保以合理的速度获得足够的数据。我无法理解什么样的实用程序提升可让我使用通过UDP发送任意大小的数据,并在另一端接收它。两台电脑都安装了Boost,两者都是linux(但不同的发行版)。个人图像丢失并不重要,但重要的是要发送或拒绝整个图像。通过boost :: asio :: udp以高数据速率发送图像数据?

  • 我是否必须手动分段要发送的数据?
  • 如果是这种情况,我该如何处理图像丢包?就像我将如何分辨我的图像的结尾是什么?我是否必须为每个数据包添加与其对应的图像行?
  • 增强是否有实用程序来处理UDP上的任意数据发送?

试图做到这一点后,我的自我(用行标识符标记每行)似乎我过度饱和一些缓冲区(标记是顺序的,直到某个点,他们开始跳跃数量单调,所以没有重新排序,但数据包丢失)注意这是在本地主机,所以我不知道我应该期待什么。

我似乎无法找到解决大量图像丢失的方法,但我不确定这是否仅仅是因为网络电缆限制(尽管发送到本地主机并在本地主机上接收?)。

我会转移到TCP,但我需要放心,我可以在另一端通过直接以太网连接可靠地获得200MB/s。图像开始发送之前的时间并不比给定秒内的整体吞吐量重要。

略微简化的代码对于由此制成:

发送图像

// sender.cpp 

#include <thread> 
#include <chrono> 
#include <algorithm> 
#include <boost/asio.hpp> 
#include <boost/array.hpp> 
#include "PgmImage.h" 

using boost::asio::ip::udp; 

const std::string &host = "localhost"; 
const std::string &port = "8000"; 
const int image_width = 2048; 
const int image_height = 2048; 

int main() { 
    try { 
     boost::asio::io_service io_service; 
     udp::resolver resolver(io_service); 
     udp::resolver::query query(udp::v4(), host, port); 
     udp::endpoint receiver_endpoint = *resolver.resolve(query); 
     udp::socket send_socket(io_service); 
     send_socket.open(udp::v4()); 
     boost::array<std::uint16_t, 2> send_header_buffer; 
     boost::array<std::uint8_t, image_height + 2> send_row_buffer; 
     auto time1 = std::chrono::high_resolution_clock::now(); 
     for (int j = 0; j < 1000; ++j) { 
      for (const auto &file_path : file_path_list) { 
       // reads in image and has a vector member of char. 
       PgmP5Image image("../short_armcam_clip/" + file_path); 
       send_header_buffer = {image.width(), image.height()}; 
       send_socket.send_to(boost::asio::buffer(send_header_buffer), receiver_endpoint); 
       for (std::uint16_t i = 0; i < image_height; i++) { 
        send_row_buffer[0] = static_cast<std::uint8_t>(i >> 8); 
        send_row_buffer[1] = static_cast<std::uint8_t>(i); 
        std::copy(&image.getRawBytes()[i * image_width], 
           &image.getRawBytes()[i * image_width + image_width], &send_row_buffer[2]); 
        send_socket.send_to(boost::asio::buffer(send_row_buffer), receiver_endpoint); 
        if (i % 64 == 0) { 
         std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1)); 
        } 
       } 
      } 
     } 
     auto time2 = std::chrono::high_resolution_clock::now(); 
     std::cout << "took : " << std::chrono::duration_cast<std::chrono::milliseconds>(time2 - time1).count() << "millis" 
        << std::endl; 
    } 
    catch (std::exception &e) { 
     std::cerr << e.what() << std::endl; 
    } 
    return 0; 
} 

接收图像

// receiver.cpp 

#include <ctime> 
#include <iostream> 
#include <string> 
#include <boost/asio.hpp> 
#include <boost/array.hpp> 
#include <iostream> 

using boost::asio::ip::udp; 

int main() { 
    try { 
     boost::asio::io_service io_service; 
     udp::socket socket(io_service, udp::endpoint(udp::v4(), 8000)); 
     udp::endpoint remote_endpoint; 

     boost::array<std::uint16_t, 2> recv_header_buffer; 
     boost::array<std::uint8_t, 2050> recv_row_buffer; 
     int num_recieved = 0; 
     while (true) { 
      boost::system::error_code error; 
      socket.receive_from(boost::asio::buffer(recv_header_buffer), remote_endpoint, 0, error); 
      if (error && (error != boost::asio::error::message_size)) { 
       throw boost::system::system_error(error); 
      } 
      int width = recv_header_buffer[0]; 
      int height = recv_header_buffer[1]; 
      std::cout << "width : " << width << " height : " << height << std::endl; 
      for (int i = 0; i < height; ++i) { 
       socket.receive_from(boost::asio::buffer(recv_row_buffer), remote_endpoint, 0, error); 
       if (error && (error != boost::asio::error::message_size)) { 
        throw boost::system::system_error(error); 
       } 
       std::uint16_t row = (recv_row_buffer[0] << 8) + recv_row_buffer[1]; 

       std::cout << "row : " << row << " processed " << std::endl; 
       std::cout << "i : " << i << std::endl; 
       if (row != i) { 
        break; 
       } 
       if (i == 2047) { 
        num_recieved += 1; 
        std::cout << "Num received: " << num_recieved << std::endl; 
       } 
      } 
     } 
    } 
    catch (std::exception &e) { 
     std::cerr << e.what() << std::endl; 
    } 
} 

  • 我必须手动分段数据发送结束了吗?

是的,你这样做。你应该对它们进行细分,使每个段适合以太网数据包(1536字节 - 以太网+ ip + udp头),这有点不方便,但是所有段可以作为矢量在asio :: buffer中传递给async_write ()。

  • 如果是这种情况,我该如何处理图像丢包?就像我将如何分辨我的图像的结尾是什么?我是否必须为每个数据包添加与其对应的图像行?

你将不得不号每个数据包,数据包#就足够了,但你的错误处理可能会最终使你的传输比TCP慢。

  • 增强是否有实用程序来处理UDP上的任意数据发送?

升压没有,据我所知有任何现成的设施,这将有助于你在UDP流数据。

我认为您最好的选择是使用TCP,它可以处理流式传输以最大限度地提高速度,并为您提供一些额外的数据完整性保障。

  • 我移动到TCP,但我需要得到保证,我可以通过在可靠的另一端直接以太网连接获得200MB /秒。图像开始发送之前的时间并不比给定秒内的整体吞吐量重要。

只有一个检查这种方式,并且运行在你的网络测试,实际的主机和目标之间。这个问题的答案取决于网络硬件和拓扑,NIC设备驱动程序和机器负载。

请注意,TCP可能会为您提供最高的吞吐量。您不必将自己的数据分段为数据包,然后您可以从boost :: asio获得最大可能的性能。

+0

经过测试TCP之后,我获得了奇怪的性能提升,所以我现在切换到TCP – snb

+0

这是因为TCP确实是流式传输。您可以通过使用套接字TCP窗口大小来优化您的应用程序以获得最佳吞吐量。 https://stackoverflow.com/questions/14381303/increasing-tcp-window-size –