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

标签 c++ image sockets boost udp

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

  • 我是否必须手动分段要发送的数据?
  • 遇到这种情况如何处理图片丢包?比如我怎么知道我的图像的结尾是什么?我是否必须用它对应的图像行标记每个数据包?
  • boost 是否有实用程序来处理通过 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;
    }
}

最佳答案

  • Do I have to manually segment data to be sent over?

是的,你知道。您应该将它们分段,以便每个段都适合一个以太网数据包(1536 字节 - 以太网 + ip + udp header ),这有点不方便,但是所有段都可以作为 vector 的 vector 传递给 asio::buffer 到 async_write ().

  • How do I deal with image packet loss if this is the case? as in how would I be able to tell what the end of my image is? Do I have to tag each packet with the row of the image it corresponds to?

您必须为每个数据包编号,一个数据包 # 就足够了,但您的错误处理可能最终会使您的传输速度比 tcp 慢。

  • Does boost have utilities to handle arbitrary data sends over UDP?

据我所知,Boost 没有任何现成的工具可以帮助您通过 UDP 传输数据。

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

  • I'd move to TCP, but I would need to be assured that I could get 200MB/s via direct Ethernet connection on the other end reliably. The time before images start be sent is less important than the overall throughput in a given second.

只有一种方法可以检查这一点,那就是在实际主机和目标之间通过网络运行测试。该问题的答案取决于网络硬件和拓扑、NIC 设备驱动程序和机器负载。

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

关于c++ - 通过 boost::asio::udp 以高数据速率发送图像数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44706430/

相关文章:

C++ 对 vector 字符串进行排序不起作用

c++ - 按位分量和中的段错误

css - 我需要在容器内将多个图像居中,每个图像都有自己的 div 标签

image - Fotorama slider - 如何延迟加载图像,然后在不可见时卸载它们?

ios - 图像不适合 tableview 下的 imageview 框架,使用 AsynchImageView 从 URL 下载

c++ - Linux socket read() 无法正确读取响应体

c++ - 如何使共享值在没有互斥锁的情况下保持一致?

c++ - 即使我在 QStyledItemDelegate 中设置 Qt::TextSelectableByMouse,QLabel 文本也无法选择

javascript - Python 套接字脚本 <-> HTML 客户端

c - 使用 kqueue 的 TCP 服务器 worker