Please do not accept this answer — accept Duck's answer.
Analysis
Your class member functions actions do not match their names.
- The constructor simply sets
umask()
and does nothing else. It doesn't even set the file descriptors to a known state.
- The
open_pipe()
function creates the FIFO if there is no file of that name.
- The
write_to_pipe()
function opens the file each time it is called and writes to the pipe without then closing it.
- The
read_from_pipe()
function opens the file each time it is called and reads from the pipe without then closing it.
- The
close_reader()
and close_writer()
functions close the file descriptor regardless of whether the corresponding read or write has been executed.
- There is no destructor.
- The
m_fp
member variable of the class is never used.
Despite this litany of problems, the code superficially should work — except that a FIFO discards any written data when all the file descriptors are closed.
You got this key information from the comment by Duck.
Note that if your code does more than one read or write before calling close, you are leaking resources horribly.
Here's a rewrite — dealing with some of the design issues.
pipe.h
#ifndef PIPE_H_INCLUDED
#define PIPE_H_INCLUDED
class Named_Pipe
{
private:
char m_name[256];
int m_rd;
int m_wr;
bool m_mk; // FIFO created?
void mkpipe(char const *name);
public:
void open_reader(const char *name);
void open_writer(const char *name);
void close_reader();
void close_writer();
void write_to_pipe(const char *msg);
int read_from_pipe(char *msg, int maxlen);
Named_Pipe();
~Named_Pipe();
};
#endif // PIPE_H_INCLUDED
pipe.cpp
#include "pipe.h"
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <fcntl.h>
#include <iostream>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
Named_Pipe::Named_Pipe() : m_rd(-1), m_wr(-1), m_mk(false)
{
m_name[0] = '\0';
umask(077);
}
Named_Pipe::~Named_Pipe()
{
close_reader();
close_writer();
unlink(m_name);
}
void Named_Pipe::mkpipe(char const *name)
{
struct stat pipe_exist;
strcpy(m_name, name);
if (stat(name, &pipe_exist) != 0)
{
if (mkfifo(name, S_IRWXU) != 0)
{
cerr << strerror(errno);
exit(1);
}
}
m_mk = true;
}
void Named_Pipe::open_reader(char const *name)
{
if (!m_mk)
mkpipe(name);
m_rd = open(m_name, O_RDONLY);
if (m_rd == -1)
{
cerr << "Error in opening " << name << " for reading" << endl;
exit(1);
}
}
void Named_Pipe::open_writer(char const *name)
{
if (!m_mk)
mkpipe(name);
m_wr = open(m_name, O_WRONLY);
if (m_wr == -1)
{
cerr << "Error in opening FIFO " << name << " for writing" << endl;
exit(1);
}
}
void Named_Pipe::write_to_pipe(char const *msg)
{
if (m_wr == -1)
{
cerr << "Writing to unopened FIFO\n";
exit(1);
}
int len = strlen(msg) + 1;
if (write(m_wr, msg, len) != len)
{
cerr << "Error in writing the message" << endl;
exit(1);
}
}
int Named_Pipe::read_from_pipe(char *msg, int msglen)
{
if (m_rd == -1)
{
cerr << "Reading from unopened FIFO\n";
exit(1);
}
int rc = read(m_rd, msg, msglen - 1);
if (rc == -1)
cerr << "Error in reading the message" << endl;
else if (rc == 0)
cerr << "EOF on pipe" << endl;
else if (msg[rc-1] != '\0')
msg[rc] = '\0';
cerr << "Read " << rc << " bytes from FIFO\n";
return rc;
}
void Named_Pipe::close_reader()
{
if (m_rd != -1)
{
close(m_rd);
m_rd = -1;
}
}
void Named_Pipe::close_writer()
{
if (m_wr != -1)
{
close(m_wr);
m_wr = -1;
}
}
pipe-reader.cpp
#include "pipe.h"
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
Named_Pipe read_process;
char buffer[256];
int i = 0;
read_process.open_reader("FIRST_FIFO_PROG");
for (i = 0; i < 10; i++)
{
int nbytes = read_process.read_from_pipe(buffer, sizeof(buffer));
const char *data = buffer;
int counter = 0;
while (nbytes > 0)
{
int len = strlen(data);
cout << "Reader" << counter << ": [" << data << "]" << endl;
nbytes -= len + 1;
data += len + 1;
}
}
read_process.close_reader();
cout << "Reader complete\n";
return 0;
}
pipe-writer.cpp
#include "pipe.h"
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
Named_Pipe write_process;
char buffer[256];
write_process.open_writer("FIRST_FIFO_PROG");
for (int i = 0; i < 10; i++)
{
sprintf(buffer, "Message on FIFO %d", i);
cout << "Write to Buffer = [" << buffer << "]" << endl;
write_process.write_to_pipe(buffer);
}
write_process.close_writer();
cout << "Writer complete\n";
return 0;
}
Sample outputs
Example 1:
$ pipe-writer & sleep 1 ; pipe-reader
[1] 9576
Write to Buffer = [Message on FIFO 0]
Write to Buffer = [Message on FIFO 1]
Write to Buffer = [Message on FIFO 2]
Write to Buffer = [Message on FIFO 3]
Write to Buffer = [Message on FIFO 4]
Write to Buffer = [Message on FIFO 5]
Write to Buffer = [Message on FIFO 6]
Write to Buffer = [Message on FIFO 7]
Write to Buffer = [Message on FIFO 8]
Write to Buffer = [Message on FIFO 9]
Writer complete
Read 180 bytes from FIFO
Reader0: [Message on FIFO 0]
Reader1: [Message on FIFO 1]
Reader2: [Message on FIFO 2]
Reader3: [Message on FIFO 3]
Reader4: [Message on FIFO 4]
Reader5: [Message on FIFO 5]
Reader6: [Message on FIFO 6]
Reader7: [Message on FIFO 7]
Reader8: [Message on FIFO 8]
Reader9: [Message on FIFO 9]
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
Reader complete
[1]+ Done pipe-writer
$
Example 2:
$ pipe-writer & pipe-reader
[1] 9579
Write to Buffer = [Message on FIFO 0]
Write to Buffer = [Message on FIFO 1]
Write to Buffer = [Message on FIFO 2]
Write to Buffer = [Message on FIFO 3]
Write to Buffer = [Message on FIFO 4]
Write to Buffer = [Message on FIFO 5]
Read Write to Buffer = [Message on FIFO 6]
Write to Buffer = [Message on FIFO 7]
Write to Buffer = [Message on FIFO 8]
Write to Buffer = [Message on FIFO 9]
36Writer complete
bytes from FIFO
Reader0: [Message on FIFO 0]
Reader1: [Message on FIFO 1]
Read 144 bytes from FIFO
Reader0: [Message on FIFO 2]
Reader1: [Message on FIFO 3]
Reader2: [Message on FIFO 4]
Reader3: [Message on FIFO 5]
Reader4: [Message on FIFO 6]
Reader5: [Message on FIFO 7]
Reader6: [Message on FIFO 8]
Reader7: [Message on FIFO 9]
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
EOF on pipe
Read 0 bytes from FIFO
Reader complete
[1]+ Done pipe-writer
$
Now you can see why there's the extra loop in the reader main program.
I still don't regard this is as a good design. There should be just one file descriptor in the class. The name of the FIFO should be passed into the constructor, along with an indication of whether this is for reading or writing. The name of the FIFO is replicated in the two programs; a common name should be used. I kept the close_xxxxer()
functions, but it would be better to let the destructor do the job. You would simplify the function names to read()
and write()
. The code error checks but writes to cerr
(not cout
) and exits on error. Note that the return code of -1 is not the same as the error number got from errno
.