C++ 为什么读取系统调用在缺少小于块时停止读取? 导言和总目标

C++ 为什么读取系统调用在缺少小于块时停止读取? 导言和总目标,c++,posix,ipc,system-calls,C++,Posix,Ipc,System Calls,我正在尝试从子进程(通过从父进程调用popen生成)向父进程发送图像 该图像是灰度png图像。它使用OpenCV库打开,并使用同一库的imencode函数进行编码。因此,生成的编码数据被存储到std::vector类型为uchar的结构中,即下面代码中的buf向量 发送初步图像信息时没有错误 首先,子对象发送父对象所需的以下图像信息: 包含编码数据的buf向量的大小:需要这段信息,以便父级将分配一个大小相同的缓冲区,用于写入它将从子级接收的图像信息。分配按如下方式执行(buf在这种情况下是用于

我正在尝试从子进程(通过从父进程调用
popen
生成)向父进程发送图像

该图像是灰度
png
图像。它使用OpenCV库打开,并使用同一库的
imencode
函数进行编码。因此,生成的编码数据被存储到
std::vector
类型为
uchar
的结构中,即下面代码中的
buf
向量

发送初步图像信息时没有错误 首先,子对象发送父对象所需的以下图像信息:

  • 包含编码数据的
    buf
    向量的大小:需要这段信息,以便父级将分配一个大小相同的缓冲区,用于写入它将从子级接收的图像信息。分配按如下方式执行(
    buf
    在这种情况下是用于接收数据的数组,而不是包含编码数据的向量):

  • 原始图像的行数:接收到所有数据后,父级解码图像所需的行数

  • 原始图像的列数:接收到所有数据后,父级解码图像所需的列数

这些数据由子级使用
cout
写入标准输出,由父级使用
fgets
系统调用读取

这些信息正确发送和接收,因此到目前为止没有问题

发送图像数据 子级使用
write
系统调用将编码数据(即向量
buf
中包含的数据)写入标准输出,而父级使用
popen
返回的文件描述符读取数据。使用
read
系统调用读取数据

数据写入和读取在while循环中以
4096
字节的块执行。书写行如下所示:

written += write(STDOUT_FILENO, buf.data()+written, s);
#include <unistd.h> //STDOUT_FILENO
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;

#define BUFLEN 4096

int main(int argc, char *argv[])
{
    Mat frame;
    std::vector<uchar> buf;
    //read image as grayscale
    frame = imread("test.png",0);
    //encode image and put data into the vector buf
    imencode(".png",frame, buf);
    //send the total size of vector to parent
    cout<<buf.size()<<endl;
    unsigned int written= 0;

    int i = 0;
    size_t toWrite = 0;
    //send until all bytes have been sent
    while (written<buf.size())
    {
        //send the current block of data
        toWrite = BUFLEN < (buf.size()-written) ? BUFLEN : (buf.size()-written);
        written += write(STDOUT_FILENO, buf.data()+written, toWrite);
        i++;
    }
    return 0;

}
其中
STDOUT\u FILENO
指示在标准输出上写入。
buf.data()
返回指向向量结构内部使用的数组中第一个元素的指针。
writed
存储到目前为止已写入的字节数,并将其用作索引
s
write
每次尝试发送的字节数(
4096
)。
write
返回实际写入的字节数,用于更新
write

数据读取非常类似,通过以下行执行:

bytes_read = read(fileno(fp), buf+total_bytes, bytes2Copy);
fileno(fp)
指示从何处读取数据(
fp
是由
popen
返回的文件描述符)
buf
是存储接收数据的数组,
total_bytes
是迄今为止读取的字节数,因此用作索引
bytes2Copy
是预期接收的字节数:它是wither
BUFLEN
(即
4096
)或最后一个数据块的剩余数据(例如,如果总字节数为
5000
,则在一个
4096
字节块后,预期另一个
5000-4096
字节块)

代码 考虑这个例子。以下是使用
popen

#include <stdlib.h>
#include <unistd.h>//read
#include "opencv2/opencv.hpp"
#include <iostream>
#define BUFLEN 4096

int main(int argc, char *argv[])
{
    //file descriptor to the child process
    FILE *fp;
    cv::Mat frame;
    char temp[10];
    size_t bytes_read_tihs_loop = 0;
    size_t total_bytes_read = 0;
    //launch the child process with popen
    if ((fp = popen("/path/to/child", "r")) == NULL)
    {
        //error
        return 1;
    }

    //read the number of btyes of encoded image data
    fgets(temp, 10, fp);
    //convert the string to int
    size_t bytesToRead = atoi((char*)temp);

    //allocate memory where to store encoded iamge data that will be received
    u_char *buf = (u_char*)malloc(bytesToRead*sizeof(u_char));

    //some prints
    std::cout<<bytesToRead<<std::endl;

    //initialize the number of bytes read to 0
    bytes_read_tihs_loop=0;
    int bytes2Copy;
    printf ("bytesToRead: %ld\n",bytesToRead);
    bytes2Copy = BUFLEN;
    while(total_bytes_read<bytesToRead &&
        (bytes_read_tihs_loop = read(fileno(fp), buf+total_bytes_read, bytes2Copy))
    )
    {
        //bytes to be read at this iteration: either 4096 or the remaining (bytesToRead-total)
        bytes2Copy = BUFLEN < (bytesToRead-total_bytes_read) ? BUFLEN : (bytesToRead-total_bytes_read);
        printf("%d btytes to copy\n", bytes2Copy);
        //read the bytes
        printf("%ld bytes read\n", bytes_read_tihs_loop);

        //update the number of bytes read
        total_bytes_read += bytes_read_tihs_loop;
        printf("%lu total bytes read\n\n", total_bytes_read);
    }
    printf("%lu bytes received over %lu expected\n", total_bytes_read, bytesToRead);
    printf("%lu final bytes read\n", total_bytes_read);
    pclose(fp);
    cv::namedWindow( "win", cv::WINDOW_AUTOSIZE );
    frame  = cv::imdecode(cv::Mat(1,total_bytes_read,0, buf), 0);
    cv::imshow("win", frame);

    return 0;

}
为什么
不读取
所有缺失的字节

我正在制作这张图片:

可能也有错误,我试图解码回图像,所以任何帮助将不胜感激

编辑

在我看来,与一些建议相反,这个问题与
\n
\r
\0
的存在无关

事实上,当我将接收到的数据打印为整数时,使用以下行:

for (int ii=0; ii<val; ii++)
{
    std::cout<<(int)buf[ii]<< " ";
}

for(int ii=0;ii您正在将二进制数据写入标准输出,这需要文本。换行符(
\n
)和/或返回字符(
\r
)可以添加或删除,具体取决于您的系统对文本文件行尾的编码。由于您缺少字符,系统似乎正在删除这两个字符中的一个


您需要将数据写入以二进制模式打开的文件,并且应该以二进制读取文件。

您正在将二进制数据写入标准输出,标准输出需要文本。换行符(
\n
)和/或返回字符(
\r
)可以添加或删除,具体取决于您的系统对文本文件行尾的编码。由于您缺少字符,系统似乎正在删除这两个字符中的一个


您需要将数据写入以二进制模式打开的文件,并以二进制方式读取文件。

更新的答案

<>我不是世界上最好的C++,但是这个工作会给你一个合理的起点。
parent.cpp

#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include "opencv2/opencv.hpp"


int main(int argc, char *argv[])
{
    // File descriptor to the child process
    FILE *fp;

    // Launch the child process with popen
    if ((fp = popen("./child", "r")) == NULL)
    {
        return 1;
    }

    // Read the number of bytes of encoded image data
    std::size_t filesize;
    fread(&filesize, sizeof(filesize), 1, fp);
    std::cout << "Filesize: " << filesize << std::endl;

    // Allocate memory to store encoded image data that will be received
    std::vector<uint8_t> buffer(filesize);

    int bufferoffset   = 0;
    int bytesremaining = filesize;
    while(bytesremaining>0)
    {
        std::cout << "Attempting to read: " << bytesremaining << std::endl;
        int bytesread   = fread(&buffer[bufferoffset],1,bytesremaining,fp);
        bufferoffset   += bytesread;
        bytesremaining -= bytesread;
        std::cout << "Bytesread/remaining: " << bytesread << "/" << bytesremaining << std::endl;
    }
    pclose(fp);

    // Display that image
    cv::Mat frame;
    frame = cv::imdecode(buffer, -CV_LOAD_IMAGE_ANYDEPTH);
    cv::imshow("win", frame);
    cv::waitKey(0);
}
#include <cstdio>
#include <cstdint>
#include <vector>
#include <fstream>
#include <cassert>
#include <iostream>

int main()
{
    std::FILE* fp = std::fopen("image.png", "rb");
    assert(fp);

    // Seek to end to get filesize
    std::fseek(fp, 0, SEEK_END);
    std::size_t filesize = std::ftell(fp);

    // Rewind to beginning, allocate buffer and slurp entire file
    std::fseek(fp, 0, SEEK_SET);
    std::vector<uint8_t> buffer(filesize);
    std::fread(buffer.data(), sizeof(uint8_t), buffer.size(), fp);
    std::fclose(fp);

    // Write filesize to stdout, followed by PNG image
    std::cout.write((const char*)&filesize,sizeof(filesize));
    std::cout.write((const char*)buffer.data(),filesize);
}
原始答案

有几个问题:

您的while循环从子进程写入数据不正确:

while (written<buf.size())
{
    //send the current block of data
    written += write(STDOUT_FILENO, buf.data()+written, s);
    i++;
}

while(书面更新答案

<>我不是世界上最好的C++,但是这个工作会给你一个合理的起点。
parent.cpp

#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include "opencv2/opencv.hpp"


int main(int argc, char *argv[])
{
    // File descriptor to the child process
    FILE *fp;

    // Launch the child process with popen
    if ((fp = popen("./child", "r")) == NULL)
    {
        return 1;
    }

    // Read the number of bytes of encoded image data
    std::size_t filesize;
    fread(&filesize, sizeof(filesize), 1, fp);
    std::cout << "Filesize: " << filesize << std::endl;

    // Allocate memory to store encoded image data that will be received
    std::vector<uint8_t> buffer(filesize);

    int bufferoffset   = 0;
    int bytesremaining = filesize;
    while(bytesremaining>0)
    {
        std::cout << "Attempting to read: " << bytesremaining << std::endl;
        int bytesread   = fread(&buffer[bufferoffset],1,bytesremaining,fp);
        bufferoffset   += bytesread;
        bytesremaining -= bytesread;
        std::cout << "Bytesread/remaining: " << bytesread << "/" << bytesremaining << std::endl;
    }
    pclose(fp);

    // Display that image
    cv::Mat frame;
    frame = cv::imdecode(buffer, -CV_LOAD_IMAGE_ANYDEPTH);
    cv::imshow("win", frame);
    cv::waitKey(0);
}
#include <cstdio>
#include <cstdint>
#include <vector>
#include <fstream>
#include <cassert>
#include <iostream>

int main()
{
    std::FILE* fp = std::fopen("image.png", "rb");
    assert(fp);

    // Seek to end to get filesize
    std::fseek(fp, 0, SEEK_END);
    std::size_t filesize = std::ftell(fp);

    // Rewind to beginning, allocate buffer and slurp entire file
    std::fseek(fp, 0, SEEK_SET);
    std::vector<uint8_t> buffer(filesize);
    std::fread(buffer.data(), sizeof(uint8_t), buffer.size(), fp);
    std::fclose(fp);

    // Write filesize to stdout, followed by PNG image
    std::cout.write((const char*)&filesize,sizeof(filesize));
    std::cout.write((const char*)buffer.data(),filesize);
}
原始答案

有几个问题:

您的while循环从子进程写入数据不正确:

while (written<buf.size())
{
    //send the current block of data
    written += write(STDOUT_FILENO, buf.data()+written, s);
    i++;
}
while(写入)
这不可能奏效

stdio
例程是缓冲的。缓冲区由实现控制。
fgets(temp,10,fp);
将从文件中读取未知数量的字节并将其放入缓冲区。这些字节将不再被低级文件IO看到

您永远不会对两种IO样式使用同一个文件。或者使用
stdio执行所有操作<
 left = data_size;
 total = 0;
 while (left > 0 &&
        (got=read(file, buf+total, min(chunk_size, left))) > 0) {
    left -= got;
    total += got;
 }

 if (got == 0) ... // reached the end of file
 else if (got < 0) ... // encountered an error
 while (left > 0 &&
        (((got=read(file, buf+total, min(chunk_size, left))) > 0) ||
        (got < 0 && errno == EINTR))) {