c++ - 为什么 PNG 图像的标准输出有时会在 printf 中刷新到图像的一半?

标签 c++ node.js png stdout

我正在尝试通过标准输出从 C++ 将 PNG 文件发送到 Nodejs。但是,当我发送它时,当我在 NodeJS 中读取它时,它有时似乎会被截断,而我只在用 C++ 发送整个 PNG 后才刷新。是什么导致了这种行为?

我发送图片的代码:

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

然后,我在 Nodejs 中接收它并测试我接收到的内容:

this.puckTracker.stdout.on('data', (data) => {
  console.log("DATA");
  var str = data.toString();
  console.log(str);
  //first check if its an image being sent. C++ prints "image 'imageData'". So try to see if the first characters are 'image'.
  const possibleImage = str.slice(0, 5);
  console.log("POSSIBLEIMAGE: " + possibleImage);
}

我在 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.

据我所知,它似乎将每个图像都剪切了一次。 这是一个 pastebin,以防有人需要完整的日志。 (打印一些额外的东西,但这应该无关紧要。)https://pastebin.com/VJEbm6V5

最佳答案

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

fflush(stdout);

没有任何保证只有最终的 fflush将在一个 block 中发送所有数据。

你从来没有,将来也不会保证只有当你明确想要它时,stdout 才会被刷新。 stdout 或其 C++ 等价物的典型实现使用固定大小的缓冲区,当缓冲区满时自动刷新,无论您是否需要。当每个角色出门时,它都会被添加到这个固定大小的缓冲区中。当缓冲区已满时,缓冲区将刷新到输出。唯一fflush做的是明确地做,冲洗掉部分填充的缓冲区。

那么,这还不是全部。

当您从网络连接中读取时,您也无法保证您将读取所有写入的内容,在一个 block 中,即使它是在一个 block 中刷新的。套接字和管道不是这样工作的。数据之间的任何地方都可以分解成中间 block ,并一次一个 block 地传递给您的阅读过程。

//make sure C++ ONLY flushes when I say so, so no data gets broken in half.
std::setvbuf(stdout, nullptr, _IOFBF, BUFSIZ);

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

If the argument buf is NULL, only the mode is affected; a new buffer will be allocated on the next read or write operation.

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

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

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

   printf("image ");

后面放字节数,这里,在nodejs中读取,解析,然后就知道要继续读取多少字节了,直到全部搞定。

当然,请记住,出于我上面解释的原因,您的 nodejs 代码首先可以读取(不太可能,但它可能发生,并且优秀的程序员会编写正确处理所有可能性的适当代码) :

image 123

在下一个 block 中读取“40”部分,表示后面有 12340 个字节。或者,它同样可以阅读:

ima

其余的如下。

结论:您无法保证您以任何方式读取的内容始终与写入内容的字节数完全匹配,无论它在写入端如何缓冲或何时刷新。套接字和管道从来没有给你这种保证(有一些轻微的读/写语义被记录,用于管道,但那是无关紧要的)。您将需要相应地对读取端的所有内容进行编码:无论读取大小,您的代码都需要逻辑解析“图像###”,一次一个字符,确定在解析之后的空格时何时停止一个数字。解析它会得到字节数,然后您的代码将需要从逻辑上读取要遵循的确切字节数。这和第一个数据 block 可能是您首先阅读的内容。您首先想到的可能就是“i”。你永远不知道会发生什么。这就像玩彩票。你没有任何保证,但这就是事情的运作方式。不,要正确做到这一点并不容易。

关于c++ - 为什么 PNG 图像的标准输出有时会在 printf 中刷新到图像的一半?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57410896/

相关文章:

c++ - 这是重载提供与非静态成员函数相同接口(interface)的静态成员函数的优雅方法吗?

javascript - 使用 Node js 在 redis 中将结果限制为 10

javascript - 创建 NodeJS + Redis 应用程序导致错误,退出代码为 139

c# - .NET Image.Save 偶尔会生成带有错误 IDAT block 的 PNG

node.js - azure:在node.js azure函数应用程序中服务器端SVG到PNG?没有任何作用

c++ - 默认初始化和值初始化之间的区别?

C++ std::vector::const_iterator 问题

node.js - NPM-package.json#engines |如何指定Python?

css - 背景重复似乎在 IE6 中不起作用

c++ - 为什么不推荐使用 std::strstream ?