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
是预期接收的字节数:它是witherBUFLEN
(即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))) {