Mapping UNIX Pipe to C++ std::cout - c++

I am researching options of communicating processes in C++. Started with idea to bind Unix pipe to std::cout, but I could get it work. When writing directly using write(STDOUT_FILENO), I get expected result. When writing using std::cout, I get smaller and random output.
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
int main() {
int pfd[2];
if(pipe(pfd) == -1){
std::cout << "Cannot create pipe" << std::endl;
return 0;
}
int pid = fork();
if(pid == -1){
std::cout << "Error on fork: " << errno << std::endl;
} else if(pid == 0) { // Child process
if(dup2(pfd[PIPE_WRITE],STDOUT_FILENO) < 0) {
std::cout << "Cannot redirect STDOUT: " << errno << std::endl;
return 0;
}
close(pfd[PIPE_WRITE]);
for(int i = 0; i < 8; i++){
int data = i;
write(STDOUT_FILENO,&data,sizeof(int)); // Works
//std::cout << data; // Don't work
}
} else { // Parent process
close(pfd[PIPE_WRITE]);
for(int i = 0; i < 8; i++){
int data;
ssize_t status;
if((status = read(pfd[PIPE_READ],&data,sizeof(int))) != sizeof(int)) {
std::cout << "Error (" << errno << ") on read: " << status << std::endl;
return -1;
}
std::cout << data << std::endl;
}
}
return 0;
}

Lets take a closer look at your writing:
write(STDOUT_FILENO,&data,sizeof(int)); // Works
//std::cout << data; // Don't work
The first "working" version write the contents of data in raw binary form to standard output. The second "non-working" version write the value of data as text to standard output.
If the value of data is 5 then the write call will write the integer value 5 while std::cout << data will write the integer value 53 (using ASCII encoding).
This of course have implications when you read the data as a raw and binary int in the parent.
If you want write the raw binary data to std::cout you have to use std::ostream::write:
std::cout.write(reinterpret_cast<char*>(&data), sizeof data);
The above line is equivalent to the write system-call you have.
Also important to know is that writing an int in raw form will write sizeof(int) bytes, usually four. Writing a single-digit integer as text will write a single byte.
Your loop will write eight numbers, which means it will write 32 bytes (4 * 8) if using write. If you output using << to std::cout then you will write 8 bytes. When you read you will read those 8 bytes and put into two single int values, then the read call will return 0 because the pipe has been closed.
What the values of those two int values will be depends on your hardware architecture, if it's little-endian or big-endian.

Related

C++ WinSock sending files

Recently, i've been assigned a client/server project, which is basically a chat room, where files can be sent and recieved, and we can use webcams.
I'm currently working on the file transfer part, and after looking at some online tutorials, i've noticed most of them use offsets to write into their buffers, then they write the whole buffer into their new file.
To replicate that kind of code, i've set up 2 buffers, one on the client side, the other on the server side. On the server side, i read 8192 bytes from my file, into the buffer, then i send it into the client side, which recieves it, and adds it to my buffer. Problem is, after the second file transfer, every single transfer it does, it's a SOCKET_ERROR, which probably means something's not quite right.
server:
std::ifstream readFile;
readFile.open(FileName, std::ios::binary | std::ios::ate);
if (!readFile)
{
std::cout << "unable to open file" << std::endl;
}
int FileSize = readFile.tellg();
readFile.seekg(0);
int remainingBytes = 0;
uint32_t FileSize32t = (uint32_t)FileSize;
FileSize32t = htonl(FileSize32t);
send(connections[ID], (char*)&FileSize32t, sizeof(uint32_t), 0);
int sent_bytes = 0;
int offset = 0;
char data[8192];
remainingBytes = FileSize;
int i = 0;
while (i<6)
{
readFile.read(data, 8192);
if (remainingBytes < 8192)
{
sent_bytes = send(connections[ID], data+offset, remainingBytes, 0);
remainingBytes -= sent_bytes;
offset += sent_bytes;
}
else
{
sent_bytes = send(connections[ID], data+offset, 8192, 0);
if (sent_bytes == SOCKET_ERROR)
{
std::cout << "erro" << std::endl;
}
remainingBytes -= sent_bytes;
offset += sent_bytes;
std::cout <<"offset: "<< offset << std::endl;
std::cout << "Sent bytes: " << sent_bytes << std::endl;
std::cout << "remaining bytes: " << remainingBytes << std::endl;
}
i++;
}
Client:
char data[8192];
std::ofstream writeFile;
writeFile.open("Putin.jpg", std::ios::binary);
int bytesReceieved = 0;
int totalBytesReceieved = 0;
int i = 0;
while (i<6)
{
if (recvFileSize - totalBytesReceieved < 8192)
{
bytesReceieved = recv(connection, data+totalBytesReceieved, recvFileSize - totalBytesReceieved, 0);
totalBytesReceieved += bytesReceieved;
}
else
{
bytesReceieved = recv(connection, data + totalBytesReceieved, 8192, 0);
totalBytesReceieved += bytesReceieved;
std::cout << totalBytesReceieved << std::endl;
}
i++;
}
writeFile.write(data, totalBytesReceieved);
std::cout << "transferĂȘncia terminada, bytes recebidos: " << totalBytesReceieved << std::endl;
writeFile.close();
Do note that this is just a test program, and it's preety much one of my first interactions with C++. I've been told this probably isn't the best way to start off with C++, but i need this assignment done until the 15th of september, so i need to finish it regardless. If you find any errors or problems besides my original issue do feel free to point them out and if you can, explain me why it's wrong.
Thank you very much for your help.

std::wcout will not print after certain amounts of characters

I've been beating my head against a wall with this one for a while. I'm only trying to make a simple application to read out the contents of a file. Here's some of the code:
errno_t error;
if ((error = fopen_s(&f, file, "r")) == 0) {
while (true) {
std::wcout << std::endl << "NEW RUN" << std::endl;
wchar_t content[4096];
if (fgetswc(content, 4096, f) == 4096) {
std::wcout << content;
std::wcout.flush();
}
else {
std::wcout << content;
std::wcout.flush();
break;
}
}
fclose(f);
std::wcout << "PLEASE PRINT THIS NOW";
system("pause");
return 0;
}
And the custom fgetswc function:
int fgetswc(wchar_t buffer[], int count, FILE * f) {
for (int i = 0; i < count; i = i + 1) {
wchar_t c = fgetwc(f);
if (c != WEOF) {
buffer[i] = c;
} else {
return i;
}
}
return count;
}
It reads the first 4096 bytes out of the file, but then subsequent std::wcout calls will not print out to the console I have. It reads the rest of the file and ends successfully, as I can see using breakpoints and the debugger. content gets filled up every iteration. I also attempted putting in debug statements, but even those don't get printed. Am I just doing something wrong? As far as I can tell there's no special characters in my file, it's just a log file.
std::wcout << content;
This is effectively calling std::wostream::operator<<(const wchar_t *). It doesn't know that content is not a ␀-terminated string. In fact, it can't possibly know that it has valid length 4096 in the first case and some amount less in the second case (you don't save the return value of fgetswc).

Named pipe file descriptor

Currently I am making a C/C++ program for the Linux Operating system.
I want to use a named pipe to communicate a PID (process ID) between two programs.
The pipe has been created and is visible in the directory.
The Get PID program says that the file descriptor returns 3, while it should return 0 if it could open the pipe. What am I doing wrong?
Get PID
// Several includes
using namespace std;
int main(int argc, char *argv[]) {
pid_t pid;
int sig = 22;
int succesKill;
int iFIFO;
char sPID[5] = {0,1,2,3,'\0'};
iFIFO = open("IDpipe" , O_RDONLY);
if(iFIFO != 0)
{
cerr << "File descriptor does not return 0, but: " << iFIFO << endl;
return EXIT_FAILURE;
}
read(iFIFO, sPID, strlen(sPID));
cerr << "In sPID now is: " << sPID << endl;
close(iFIFO);
pid = atoi(sPID);
cout << "The PID I will send signals to is: " << pid << "." << endl;
while(1)
{
succesKill = kill(pid, sig);
cout << "Tried to send signal" << endl;
sleep(5);
}
return EXIT_SUCCESS;
}
Send PID
// Several includes
using namespace std;
void catch_function(int signo);
volatile sig_atomic_t iAmountSignals = 0;
int main(void) {
pid_t myPID;
int iFIFO;
char sPID[5] = {'l','e','e','g','\0'};
myPID = getpid();
sprintf(sPID, "%d",myPID);
cout << "My PID is: " << sPID << endl;
iFIFO = open("IDpipe" , O_WRONLY);
if(iFIFO == -1)
{
cerr << "Pipe can't be opened for writing, error: " << errno << endl;
return EXIT_FAILURE;
}
write(iFIFO, sPID, strlen(sPID));
close(iFIFO);
if (signal(22, catch_function) == SIG_ERR) {
cerr << "An error occurred while setting a signal handler." << endl;
return EXIT_FAILURE;
}
cout << "Raising the interactive attention signal." << endl;
if (raise(22) != 0) {
cerr << "Error raising the signal." << endl;
return EXIT_FAILURE;
}
while(1)
{
cout << "iAmountSignals is: " << iAmountSignals << endl;
sleep(1);
}
cout << "Exit." << endl;
return EXIT_SUCCESS;
}
void catch_function(int signo) {
switch(signo) {
case 22:
cout << "Caught a signal 22" << endl;
if(iAmountSignals == 9)
{iAmountSignals = 0;}
else
{++iAmountSignals;}
break;
default:
cerr << "Thats the wrong signal.." << endl;
break;
}
}
Terminal output
Output
open() returns the newly created file descriptor. It cannot return 0 for the simple reason that the new process already has a file descriptor 0. That would be standard input.
The return value of 3 is the expected result from open(), in this case, because that would be the next available file descriptor after standard input, output, and error. If open() couldn't open the file descriptor, it would return -1.
But besides that, your code also has a bunch of other bugs:
sprintf(sPID, "%d",myPID);
// ...
write(iFIFO, sPID, strlen(sPID));
If your process ID happens to be only 3 digits long (which is possible), this will write three bytes to the pipe.
If your process ID happens to be five digits long (which is even more possible), this will write 5 bytes plus the '\0' byte, for a total of six bytes written to the five byte-long sPID buffer, overrunning the array and resulting in undefined behavior.
The actual results are, of course, are undefined, but a typical C++ implementation will end up clobbering the first byte of whatever is the next variable on the stack, which is:
int iFIFO;
which is your file descriptor. So, if your luck runs out and your new process gets a five-digit process id, and this is a little-endian C++ implementation, there is no padding, then the low order byte of iFIFO gets set to 0, and if the code got compiled without any optimizations, the iFIFO file descriptor gets set to 0. Hillarity ensues.
Furthermore, on the other side of the pipe:
char sPID[5] = {0,1,2,3,'\0'};
// ...
read(iFIFO, sPID, strlen(sPID));
Because the first byte of SPID is always set to 0, this will always execute read(iFIFO, sPID, 0), and not read anything.
After that:
pid = atoi(sPID);
atoi() expects a '\0'-terminated string. read() only reads whatever it reads, it will not '\0'-terminate whatever it ends up reading. It is your responsibility to place a '\0' that terminates the read input (and, of course, making sure that the read buffer is big enough), before using atoi().
Your logic appears to be incorrect.
if(iFIFO != 0)
should be
if(iFIFO == -1)
since open returns -1 on error. Otherwise it returns a valid file descriptor.

get error if i use fread, while no error using read

I'm trying to make some experiments on disk I/O using cache and not using it. In order to perform a read directly from the disk, I open the file with the O_DIRECT flag (defining the variable DISK_DIRECT).
Now the two branches of the if beneath, should perform the same operation, with the difference that one is helped by the cache and the other not.
The files to which I try to access are stored on disk and they do not change over time.
Also the two branches access to the same files.
At some point here, when I use fread I get ferror() to be true. While when I use read everything goes fine.
I'm sure they access the same files.
Do you have any idea why this could happen?
EDIT
Ok, i'm posting here an minimal example. the code i use is:
#include <iostream>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <fstream>
#include <sstream>
using namespace std;
typedef float fftwf_complex [2] ;
void fetch_level(unsigned long long tid, unsigned short level, fftwf_complex* P_read, fftwf_complex* P_fread, int n_coeff_per_level, FILE** files_o_direct, fstream* & files) {
int b_read;
fseek(files_o_direct[level],(long int) (tid * sizeof(fftwf_complex)*n_coeff_per_level), SEEK_SET);
b_read = fread(reinterpret_cast<char*>(P_fread),sizeof(fftwf_complex), n_coeff_per_level,files_o_direct[level]);
if(b_read == 0){
cerr << "nothing read\n";
}
files[level].seekg((streamoff) (tid * sizeof(fftwf_complex)*n_coeff_per_level), files[level].beg);
files[level].read(reinterpret_cast<char*>(P_read),
sizeof(fftwf_complex) * n_coeff_per_level);
}
void open_files (fstream* & files){
for(int i=0; i<1;i++) {
std::ostringstream oss;
oss << "./Test_fread_read/1.txt.bin";
files[i].open(oss.str().c_str(),
std::ios::in | std::ios::out |
std::ios::binary | std::ios::ate);
if (!files[i])
{
cerr << "fstream could not open " << oss.str() << endl;
}
}
}
void open_files_o_direct (FILE** files_o_direct, int* fd){
for(unsigned int i=0;i<1; i++){
std::ostringstream oss;
oss << "./Test_fread_read/1.txt.bin";
fd[i]=open(oss.str().c_str(), O_RDONLY | O_DIRECT);
files_o_direct[i] = fdopen(fd[i], "rb");
if(!files_o_direct[i])
cerr << "Could not open " << oss.str() << endl;
}
}
int close_files(FILE** files_o_direct, int* fd, fstream* & files) {
for(unsigned int i=0; i<1; i++){
//#if defined (DISK_DIRECT)
if(files_o_direct[i])
close(fd[i]);
//#else
if(files[i].is_open())
files[i].close();
//#endif
}
return 0;
}
int main(){
FILE**files_o_direct = new FILE* [256];
fstream* files = new fstream [256];
int * fd = new int [256];
fftwf_complex * P_read = new fftwf_complex [1];
fftwf_complex * P_fread = new fftwf_complex [1];
open_files_o_direct(files_o_direct, fd);
open_files(files);
fetch_level(2, 0, P_read, P_fread, 1, files_o_direct, files);
cout << "P_read: " << P_read[0][0] << " P_fread: " << P_fread[0][0] << endl;
cout << "P_read: " << P_read[0][1] << " P_fread: " << P_fread[0][1] << endl;
fetch_level(7, 0, P_read, P_fread, 1, files_o_direct, files);
cout << "P_read: " << P_read[0][0] << " P_fread: " << P_fread[0][0] << endl;
cout << "P_read: " << P_read[0][1] << " P_fread: " << P_fread[0][1] << endl;
fetch_level(8, 0, P_read, P_fread, 1, files_o_direct, files);
cout << "P_read: " << P_read[0][0] << " P_fread: " << P_fread[0][0] << endl;
cout << "P_read: " << P_read[0][1] << " P_fread: " << P_fread[0][1] << endl;
close_files(files_o_direct, fd, files);
delete [] P_read;
delete [] P_fread;
delete [] files;
delete [] files_o_direct;
return 0;
}
and the file which is accessed is:
0.133919 0.0458176
1.67441 2.40805
0.997525 -0.279977
-2.39672 -3.076
-0.0390913 0.854464
-0.0176478 -1.3142
-0.667981 -0.486272
0.831051 0.282802
-0.638032 -0.630943
-0.669854 -1.49762
which is stored in a binary format and that can be download from here: 1.txt.bin.
The output i get is:
nothing read
P_read: 0.997525 P_fread: 0
P_read: -0.279977 P_fread: 0
nothing read
P_read: 0.831051 P_fread: 0
P_read: 0.282802 P_fread: 0
nothing read
P_read: -0.638032 P_fread: 0
P_read: -0.630943 P_fread: 0
The problem persists even if i change the type of fftwf_complex from float[2] to simple float.
If i remove the fseek line everything works correctly.
This if (b_read == 0), will be true at the end of the file, and you will enter this branch
if(ferror(this->files_o_direct[level]))
fseek(this->files_o_direct[level], 0, SEEK_END); //ftell here returns 4800000
cerr << "nothing read\n";
even if ferror returns 0, the end of the file was reached anyway
fseek(this->files_o_direct[level], 0, SEEK_END);
makes no sense, and "nothing read\n" will be output either or not ferror returns nonzero.
From the manual page
fread() does not distinguish between end-of-file and error, and callers must use feof(3) and ferror(3) to determine which occurred.
so you have to check feof and if it is false you use ferror.
For who ever may have the same problem here there is the answer:
The O_DIRECT flag may impose alignment restrictions on the length and
address of user-space buffers and the file offset of I/Os. In Linux
alignment restrictions vary by filesystem and kernel version and
might be absent entirely. However there is currently no
filesystem-independent interface for an application to discover these
restrictions for a given file or filesystem. Some filesystems
provide their own interfaces for doing so, for example the
XFS_IOC_DIOINFO operation in xfsctl(3).
Under Linux 2.4, transfer sizes, and the alignment of the user buffer
and the file offset must all be multiples of the logical block size
of the filesystem. Since Linux 2.6.0, alignment to the logical block
size of the underlying storage (typically 512 bytes) suffices. The
logical block size can be determined using the ioctl(2) BLKSSZGET
operation or from the shell using the command:
blockdev --getss
linux reference page

sorting random numbers sent to child using exec

What I am trying to do is send random numbers generated by a parent and then sent to the child, who then execs "sort -nr", and then sends back the sorted numbers back to the parent. I found this question had already been asked and answered here pretty similar to mine: how to redirect output of "sort" program from child to parent, and I thought I did everything that it said to get it to work, but I am not able to get the sorting to actually happen. I've even checked to see if it errors out, but I have gotten nothing.
Both Pipes send and receive the same numbers, but they never come out sorted. What Am I missing?
int pipe1[2], pipe2[2];
pid_t childID;
if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
childID = fork();
if (childID < 0) {
//Child Process Failure
perror("fork");
exit(EXIT_FAILURE);
}
else if (childID == 0){
//Child Process Instructions
cout << "Sent Numbers: " << endl;
//Closes Unused Pipes
close(pipe1[WRITE_END]);
close(pipe2[READ_END]);
//Dups Over the Others, then closes them
dup2(pipe1[READ_END], STDIN_FILENO);
close(pipe1[READ_END]);
dup2(pipe2[WRITE_END], STDOUT_FILENO);
close(pipe2[WRITE_END]);
int fail = execlp("sort", "sort", "-nr", (char *)NULL);
cout << fail << endl;
}
else {
//Parent Process Instructions
//Close Unused Pipes
close(pipe1[READ_END]);
close(pipe2[WRITE_END]);
srand(randSeed);
cout << "Random Numbers: " << endl;
for (int i = 0; i < nWorkers; i++){
//Generate nWorker numbers, then Write
randNumbers[i] = rand() % (sleepMax - sleepMin + 1) + sleepMin;
write(pipe1[WRITE_END], &randNumbers[i], sizeof(randNumbers[i]));
cout << randNumbers[i] << endl;
}
close(pipe1[WRITE_END]);
wait(NULL);
cout << "SORTED NUMBERS:" << endl;
double sortedNumbers[nWorkers];
int n;
for(int k = 0; k < nWorkers; k++) {
n = read(pipe2[READ_END], &sortedNumbers[k], sizeof(sortedNumbers[k]));
cout << sortedNumbers[k] << ", " << n << endl;
}
}
sort(1) expects its input to be ASCII strings, not raw binary numbers. When you're passing it the data with write(2), that's writing out the raw binary representation of the numbers to the pipes, which is not what you want. You need to convert the numbers to their string representations.
One way to do that would be to open a stdio stream on top of the pipe with fdopen(3). You could then use fprintf to write the formatted data:
FILE *childInput = fdopen(pipe1[WRITE_END], "w");
if (childInput == NULL) { /* Handle error */ }
for (...)
{
...
fprintf(childInput, "%d\n", randNumbers[i]);
}
fclose(childInput);
Likewise, you need to do the same thing when reading back in the output from the child:
FILE *childOutput = fdopen(pipe2[READ_END], "r");
if (childOutput == NULL) { /* Handle error */ }
while (fscanf(childOutput, "%d", &sortedNubers[i]) == 1)
{
...
}
fclose(childOutput);