为什么PNG图像的标准输出有时会在printf中刷新到图像的一半? 我试图从C++向STODUT发送一个PNG文件到NoDEJS。然而,当我发送它时,它似乎被削减一半有时当我在NodeJS阅读,而我只是冲洗后,我在PC++发送整个PNG。是什么导致了这种行为

为什么PNG图像的标准输出有时会在printf中刷新到图像的一半? 我试图从C++向STODUT发送一个PNG文件到NoDEJS。然而,当我发送它时,它似乎被削减一半有时当我在NodeJS阅读,而我只是冲洗后,我在PC++发送整个PNG。是什么导致了这种行为,c++,node.js,png,stdout,C++,Node.js,Png,Stdout,发送图像的我的代码: void SendImage(Mat image) { //from: https://stackoverflow.com/questions/41637438/opencv-imencode-buffer-exception std::vector<uchar> buffer; #define MB image_size.width*image_size.height buffer.resize(200 * MB); cv:

发送图像的我的代码:

void SendImage(Mat image)
{   //from: https://stackoverflow.com/questions/41637438/opencv-imencode-buffer-exception
    std::vector<uchar> buffer;
    #define MB image_size.width*image_size.height
    buffer.resize(200 * MB);
    cv::imencode(".png", image, buffer);

    printf("image ");
    for(int i = 0; i < buffer.size(); i++)
        printf("%c", buffer[i]);

    fflush(stdout);
}

我已经尝试了C++中的以下命令来尝试删除自动刷新:

    //disable sync between libraries. This makes the stdout much faster, but you must either use cout or printf, no mixes. Since printf is faster, use printf everywhere.
    std::ios_base::sync_with_stdio(false);
    //make sure C++ ONLY flushes when I say so, so no data gets broken in half.
    std::setvbuf(stdout, nullptr, _IOFBF, BUFSIZ);
当我用可见终端运行C++程序时,似乎没问题。 我希望NodeJS控制台打印的是:

DATA
image ëPNG

IHDR ... etc, all the image data.
POSSIBLEIMAGE: image
这是我发送的每一张图片

相反,我得到:

DATA
image �PNG

IHDT ...
POSSIBLEIMAGE: image
DATA
-m5VciVWjՖҬvXjvXm9kV[d嬭v
POSSIBLEIMAGE: -m5V
DATA
image �PNG
etc.
据我所知,似乎每幅图像都被剪切了一次。 这是一个粘贴箱,以防有人需要完整的日志。打印一些额外的东西,但这不重要

这不会关闭缓冲,有效地使缓冲无限。从Linux文档中了解空缓冲区指针的情况:

如果参数buf为NULL,则只影响模式;新的缓冲区 将在下一次读或写操作时分配

所有这一切只是给你一个默认的缓冲区,默认大小。反正都已经有了

现在,您当然可以创建一个与图像一样大的自定义缓冲区,这样所有内容都可以预先缓冲。但是,正如我所解释的,无论如何,这不会完成任何有用的事情。数据仍有可能在传输过程中被分解,您将在nodejs中一次读取一块数据

整个方法是完全错误的。您需要提前单独发送字节数,首先读取,然后知道预期的字节数,然后读取给定的字节数

   printf("image ");
在这里输入要跟随的字节数,在nodejs中读取它,解析它,然后知道要继续读取多少字节,直到得到所有内容

当然,请记住,出于我上面解释的原因,您的nodejs代码不太可能读取的第一件事,但它是可能发生的,一个好的程序员会编写正确的代码,正确处理所有可能的情况:

image 123
在下一个块中读取40个部分,表示后面有12340个字节。或者,也可以同样通俗地理解为:

ima
其余的都在后面

结论:您无法保证您以任何方式读取的内容始终与所写入内容的字节计数完全匹配,无论写入端如何缓冲或何时刷新。套接字和管道从来没有给过您这样的保证。对于管道,文档中有一些轻微的读/写语义,但这与此无关。您需要在读取端对所有内容进行相应的编码:无论读取的内容有多小或多大,您的代码都需要在逻辑上解析图像,每次解析一个字符,确定解析数字后的空格时何时停止。解析它会给出字节数,然后代码需要从逻辑上读取要遵循的确切字节数。这可能是您读取的第一个内容,以及第一块数据。有可能第一个认为你会读到的只是i。你永远不知道会发生什么。就像玩彩票一样。你没有任何保证,但事情就是这样。不,这不容易,要正确地做

这不会关闭缓冲,有效地使缓冲无限。从Linux文档中了解空缓冲区指针的情况:

如果参数buf为NULL,则只影响模式;新的缓冲区 将在下一次读或写操作时分配

所有这一切只是给你一个默认的缓冲区,默认大小。反正都已经有了

现在,您当然可以创建一个与图像一样大的自定义缓冲区,这样所有内容都可以预先缓冲。但是,正如我所解释的,无论如何,这不会完成任何有用的事情。数据仍有可能在传输过程中被分解,您将在nodejs中一次读取一块数据

整个方法是完全错误的。您需要提前单独发送字节数,首先读取,然后知道预期的字节数,然后读取给定的字节数

   printf("image ");
在这里输入要跟随的字节数,在nodejs中读取它,解析它,然后知道要继续读取多少字节,直到得到所有内容

当然,请记住,出于我上面解释的原因,您的nodejs代码不太可能读取的第一件事,但它是可能发生的,一个好的程序员会编写正确的代码,正确处理所有可能的情况:

image 123
在下一个块中读取40个部分,表示后面有12340个字节。或者,也可以同样通俗地理解为:

ima
其余的都在后面

结论:您无法保证您以任何方式读取的内容始终与所写入内容的字节计数完全匹配,无论写入端如何缓冲或何时刷新。插座和管道从来没有给过你这个 保证有一些轻微的读/写语义是为管道记录的,但这是无关的。您需要在读取端对所有内容进行相应的编码:无论读取的内容有多小或多大,您的代码都需要在逻辑上解析图像,每次解析一个字符,确定解析数字后的空格时何时停止。解析它会给出字节数,然后代码需要从逻辑上读取要遵循的确切字节数。这可能是您读取的第一个内容,以及第一块数据。有可能第一个认为你会读到的只是i。你永远不知道会发生什么。就像玩彩票一样。你没有任何保证,但事情就是这样。不,这不容易,要正确操作。

我已经修好了,现在可以用了。我把我的代码放在这里,以防功能中有人需要它

发送端C++ 为了能够连接缓冲区并正确解析它,我在发送的消息周围添加了stArt和eNd。示例:stArtimage‰PNG..IHDR..binary data..eNd。 您也可以使用PNG本身的默认开始和停止,甚至在下一次开始之前只使用开始和获取所有内容。但是,我也需要发送自定义数据。C++代码现在是:

void SendImage(Mat image)
{
    std::vector<uchar> buffer;
    cv::imencode(".png", image, buffer);

    //StArt (that caps) is the word to split the data chunks on in nodejs.
    cout << "stArtimage";
    fwrite(buffer.data(), 1, buffer.size(), stdout);
    cout << "eNd";
    fflush(stdout);
}
客户端Javascript 要使答案完整,要在浏览器中呈现PNG,请使用以下代码,并确保在HTML中准备好画布

socket.on('PNG', (PNG) => {
    var blob = new Blob([PNG], { type: "image/png" });
    var img = new Image();
    var c = document.getElementById("canvas");
    var ctx = c.getContext("2d");


    img.onload = function (e) {
        console.log("PNG Loaded");
        ctx.drawImage(img, 0, 0);
        window.URL.revokeObjectURL(img.src);
        img = null;
    };

    img.onerror = img.onabort = function (error) {
        console.error("ERROR!", error);
        img = null;
    };
    img.src = window.URL.createObjectURL(blob);
});
请确保不要经常使用SendImage,否则会使标准输出和连接溢出数据,并且打印出来的速度会快于浏览器或服务器的处理速度。

我已经修复了它,它现在可以工作了。我把我的代码放在这里,以防功能中有人需要它

发送端C++ 为了能够连接缓冲区并正确解析它,我在发送的消息周围添加了stArt和eNd。示例:stArtimage‰PNG..IHDR..binary data..eNd。 您也可以使用PNG本身的默认开始和停止,甚至在下一次开始之前只使用开始和获取所有内容。但是,我也需要发送自定义数据。C++代码现在是:

void SendImage(Mat image)
{
    std::vector<uchar> buffer;
    cv::imencode(".png", image, buffer);

    //StArt (that caps) is the word to split the data chunks on in nodejs.
    cout << "stArtimage";
    fwrite(buffer.data(), 1, buffer.size(), stdout);
    cout << "eNd";
    fflush(stdout);
}
客户端Javascript 要使答案完整,要在浏览器中呈现PNG,请使用以下代码,并确保在HTML中准备好画布

socket.on('PNG', (PNG) => {
    var blob = new Blob([PNG], { type: "image/png" });
    var img = new Image();
    var c = document.getElementById("canvas");
    var ctx = c.getContext("2d");


    img.onload = function (e) {
        console.log("PNG Loaded");
        ctx.drawImage(img, 0, 0);
        window.URL.revokeObjectURL(img.src);
        img = null;
    };

    img.onerror = img.onabort = function (error) {
        console.error("ERROR!", error);
        img = null;
    };
    img.src = window.URL.createObjectURL(blob);
});

请确保不要经常使用SendImage,否则会使标准输出和连接溢出数据,并且打印速度会超过浏览器或服务器的处理速度。

为什么要首先打印二进制数据?这是XY问题吗?你的假设是错误的:仅仅因为C++程序刷新一次并不意味着你的NoDEJs能一口气读完它。切换到适当的成帧方法,即预先告诉您将跟随多少字节的方法:不要使用printf逐字节打印图像。只需使用fwrite或write系统调用。@amberleferink只需将char*buffer.data传递给fwrite。删除buffer.resize,缓冲区的大小将根据cv::IMENCODEW的需要进行调整。您为什么要首先打印二进制数据?这是XY问题吗?你的假设是错误的:仅仅因为C++程序刷新一次并不意味着你的NoDEJs能一口气读完它。切换到适当的成帧方法,即预先告诉您将跟随多少字节的方法:不要使用printf逐字节打印图像。只需使用fwrite或write系统调用。@amberleferink只需将char*buffer.data传递给fwrite。删除buffer.resize,cv::imencodeUsing image 12345 as header(类似于HTTP分块编码)将根据需要调整缓冲区的大小。使用image 12345 as header(类似于HTTP分块编码)可提供明确的分析。使用image 12345 as header(类似于HTTP分块编码)可提供明确的分析。