0

I'm having some issues using write and read functions to interact with a FIFO file, for some reason, when I read the FIFO, I get junk in the readed object (I'm making a minitwitter, so using client-server model):

Client-side:

Breakpoint 1, main (argc=3, argv=0x7fffffffdf88) at Cliente.cpp:50
50      if(write (fdReceiverPipe, registerMessage, sizeof (*registerMessage)) == -1){
(gdb) p registerMessage
$3 = (Mensaje *) 0x605030
(gdb) p *registerMessage
$4 = {sender = 1, mensaje = "21870", tipoRequest = 0}

Server-side:

Breakpoint 1, MessagePipeHandler (
messageRequestCounter=0x606324 <messageRequestCounter>) at Gestor.cpp:166
166       int statusMessagePipe = read (fdMessagePipe, mensaje, sizeof *mensaje);
(gdb) p sizeof (mensaje)
$1 = 8
(gdb) p sizeof (*mensaje)
$2 = 24
(gdb) p *mensaje
$3 = {sender = -144108792, mensaje = "", tipoRequest = 6320304}

For avoiding junk entering the pipe, the client-to-server pipe O_NONBLOCK option is clear and the server-to-client pipe is readed when the server sends a signal, here is the code:

Server-side:

//Function for pipe for messages check and processing
void MessagePipeHandler (int *messageRequestCounter) {
  //Open the message pipe
  std :: cout << "Abriendo pipe " << mainPipeName << std :: endl;
  int fdMessagePipe = open (mainPipeName, O_RDONLY);
  if (fdMessagePipe == -1) {
    std :: cout << "No se pudo abrir el pipe para los mensaje, ¿Existe el archivo del pipe?" << std :: endl;
    exit (EXIT_FAILURE);
  } else {
    //Activate message pipe service
    std :: cout << "¡Servicio de pipe de mensajes online!" << std :: endl;
    int statusMessagePipe;
    while (true) {
      Mensaje *mensaje = new Mensaje ();
      int statusMessagePipe = read (fdMessagePipe, mensaje, sizeof (*mensaje));
      if (statusMessagePipe == -1) {
        std :: cout << "Error al leer el archivo de pipe..." << std :: endl;
        exit (EXIT_FAILURE);
      } else if (statusMessagePipe > 0) {
        //Check message type
        switch (mensaje -> getTipoRequest ()) {
          //Connection request
          case 0:
            IDMessageHandler (mensaje);
            break;
          //Follow
          case 1:
            server -> setRelation (mensaje -> getSender (), atoi (mensaje -> getMensaje().c_str()), '1');
            break;
          //Unfollow
          case 2:
            server -> setRelation (mensaje -> getSender (), atoi (mensaje -> getMensaje().c_str()), '0');
            break;
          //Tweet
          case 3:
            DispatchTweet (mensaje);
            break;
          //Disconnect
          case 5:
            server -> Disconnect (mensaje -> getSender ());
            break;
          //Bad request
          default:
            std :: cout << "El tipo de request es desconocido, continuando..." << std :: endl;
            break;
        }
        (*messageRequestCounter) ++;
      }
      mensaje -> Dispose ();
    }
  }
}

Client-side:

int main (int argc, char** argv){
    //Installing signal handler
    signal (SIGUSR1, (sighandler_t) signalHandler);
    //See if IDPipe is created
    receiverPipe = argv[2];
    int fdReceiverPipe = open (receiverPipe, O_WRONLY);
    std :: cout << fdReceiverPipe << std :: endl;
    if (fdReceiverPipe > 0) std :: cout << "Pipe de IDs encontrado, registrandose..." << std :: endl;
    else {
        std :: cout << "No se ha encontrado el pipe, ¿esta encendido el servidor?" << std :: endl;
        exit (EXIT_FAILURE);
    }
    //Register the IDs on the pipe
    Mensaje *registerMessage = new Mensaje ();
    std :: ostringstream itos;
    itos << getpid ();
    registerMessage -> setSender (atoi (argv[1]));
    registerMessage -> setMensaje (itos.str ());
    registerMessage -> setTipoRequest (0);
    if(write (fdReceiverPipe, registerMessage, sizeof (*registerMessage)) == -1){
        std :: cout << "Se ha presentado un problema al registrar el cliente." << std :: endl;
        exit (EXIT_FAILURE);
    } else std :: cout << "¡Se ha registrado el cliente con exito!" << std :: endl;
    //Set client info and see server mode
    pause ();
    cliente -> setId (atoi (argv [1]));
    cliente -> setPid (getpid ());
    std :: cout << "Recibida respuesta, obteniendo configuracion del servidor..." << std :: endl;
    Mensaje *serverMessage = new Mensaje ();
    std :: string senderPipe ("SenderPipe");
    std :: string userId (argv [1]);
    senderPipe += userId;
    int fdSenderPipe = open (senderPipe.c_str (), O_RDONLY | O_NONBLOCK);
    if (fdSenderPipe == -1) {
        std :: cout << "No se logro acceder a la configuracion del servidor, ¿esta encendido el servidor?" << std :: endl;
        exit (EXIT_FAILURE);
    } else {
        std :: cout << "Tomando datos del pipe canal, un momento..." << std :: endl;
        for (int tries = 0; tries < MAX_TRIES; tries ++) {
            int pipeState = read (fdSenderPipe, serverMessage, sizeof *serverMessage);
            if (pipeState == -1) {
                std :: cout << "No hay configuracion del servidor, reintentando..." << std :: endl;
                sleep (2000);
            } else {
                if(serverMessage -> getMensaje () == "async") {
                    std :: cout << "Configuracion completada." << std :: endl;
                    Pause();
                    break;
                } else {
                    serverModeAsync = false;
                    std :: cout << "Configuracion completada." << std :: endl;
                    Pause();
                    break;
                }
            }
        }
    }
    std :: cout << "Llamando al menu..." << std :: endl;
    Menu ();
}

I have two questions: 1) Is it possible to use write () and read () for object I/O operations in binary files? 2) If not, is there any workaround so I may maintain the OOP I'm using?

Thanks in advance.

EDIT:

Message class:

#ifndef __Mensaje__HXX__
#define __Mensaje__HXX__

#include<iostream>

class Mensaje{
    public:
        Mensaje();
        ~Mensaje();
        int getSender();
        void setSender(int sender);
        std :: string getMensaje();
        void setMensaje(std::string msg);
        int getTipoRequest();
        void setTipoRequest(int tipoRequest);
        void Dispose ();
    private:
        int sender;
        std :: string mensaje;
        int tipoRequest;
};

#include "Mensaje.cxx"

#endif
Nevinyrral
  • 17
  • 5
  • 1
    Let's boil it down to this line: `read (fdMessagePipe, mensaje, sizeof (*mensaje))` -- What did you expect this line to do? Whatever it is, it isn't what you think. C++ is not C. More info -- http://en.cppreference.com/w/cpp/concept/PODType Your type is *not* POD. – PaulMcKenzie Nov 15 '15 at 02:23
  • http://stackoverflow.com/questions/523872/how-do-you-serialize-an-object-in-c – PaulMcKenzie Nov 15 '15 at 02:31
  • @PaulMcKenzie I know that read is a C function, that's why I'm asking if is it possible to use a C I/O operation that reads/writes data in a binary file like a FIFO and a instance of a class using C++, read and write needs a file descriptor, a pointer to a memory location, and the size of the content, I'm in a learning process and I though it might be possible – Nevinyrral Nov 15 '15 at 02:32
  • @PaulMcKenzie then should I first serialize the object, then write the bytes result in the FIFO, read and deserialize? – Nevinyrral Nov 15 '15 at 02:37
  • Let's make it simple. How would you serialize a `std::string`? Your goal is to take an existing object and save it to a file. Then you have to take the contents of that file, and recreate an object during the read. If you looked at the file you did create, is there any way you could take those contents and magically create a `mensaje` from it? Of course it's junk because you saved junk. You have to save the *data*, not the object. – PaulMcKenzie Nov 15 '15 at 02:43

3 Answers3

0

The class Mensaje has a std::string data member. Typically a std::string instance is or contains a pointer to the string's buffer. Sending a pointer to another process and trying to use it there, is not going to work.

More formally, Mensaje is not guaranteed a standard-layout class, due to the not-guaranteed-standard-layout string data member.


What matters is what you send to the other process. Don't send a pointer: send the text data that it points to.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • So if I replace the string data member with and char * or static char array it may work? – Nevinyrral Nov 15 '15 at 02:41
  • `char*` would suffer the same problem: that's even more directly a pointer. But what matters is what you send to the other process. Don't send the pointer: send that text data that it points to. – Cheers and hth. - Alf Nov 15 '15 at 02:42
  • Yep, dumb question there, I'll use a static char array then, thank you very much! – Nevinyrral Nov 15 '15 at 02:45
  • @Nevinyrral I don't think you clearly understand the underlying issue. It isn't just the char array. If the class is non-POD, you can't save it to a binary file as you're doing now. Add a vector, or a virtual destructor, or another type that isn't POD, and you're back to square one. – PaulMcKenzie Nov 15 '15 at 02:48
  • Then the ~Mensaje () is also an issue, even if is it empty? Sorry trying to get the gist of this problem – Nevinyrral Nov 15 '15 at 03:03
  • The destructor makes the class non-*trivial*. In order to be POD (Plain Old Data) the class must be both trivial and standard layout. So, a non-trivial destructor is a non-POD feature. But that's not the main issue. The main issue is to send data, not pointers to data. – Cheers and hth. - Alf Nov 15 '15 at 03:10
  • OK, I was reading http://stackoverflow.com/questions/4178175/what-are-aggregates-and-pods-and-how-why-are-they-special, for what I understand of it is that first by using custom destructors/constructors = bad idea, second, no non-static data members (so the string is a no go), and third using Mensaje *mensaje = new Mensaje(); is also a bad idea (use {} for initialization), is this correct? – Nevinyrral Nov 15 '15 at 03:45
0

You tagged this question as C++.

1) Is it possible to use write () and read () for object I/O operations in binary files? 2) If not, is there any workaround so I may maintain the OOP I'm using?

1 - Yes.

2 - n/a

Example: (the read() and write() below are not 'c' functions)

// m_sIn is std::ifstream
// char*  m_buff
// MAX_buffSz is an enum (in this case)

m_sIn.read (m_buff, MAX_buffSz);

See def of std::ifstream::read( char* s, bytecount)

Extracts n characters from the stream and stores them in the array pointed to by s.

This function simply copies a block of data, without checking its contents nor appending a null character at the end.

std::ofstream::write() is analogous.

read() of ifstream (and write() of ofstream) are binary i/o.

2785528
  • 5,438
  • 2
  • 18
  • 20
  • Thank you for your response, however I changed my class to a POD class, I wish I had seen this before. I'm going to try it though, is a really good solution. – Nevinyrral Nov 15 '15 at 15:35
  • @Nevinyrral - yes, POD is required if you wish to transfer classes or structs. Been there, done that. ALSO note that C++ compilers are allowed (I think not required) to add a byte (or more) to any class/struct INCLUDING POD, when the class/struct has a virtual method ... I never got the class with virtual methods to transfer. – 2785528 Nov 15 '15 at 17:45
  • @Nevinyrral - There is a technique for converting to a POD (from a non-pod). I worked through this when I needed "persistant store" in an embedded system, and have used it for shared memory objects. I think this technique also supports what you need. It is _more_ code for you to write and debug, so if you can make do with simple POD (and no virtual methods), save yourself the effort. Good luck. – 2785528 Nov 15 '15 at 18:10
0

OK, I modified my Mensaje class:

#ifndef __Mensaje__HXX__
#define __Mensaje__HXX__

#include<iostream>

class Mensaje{
    public:
        int getSender();
        void setSender(int sender);
        void getMensaje(char message[]);
        void setMensaje(const char *message);
        int getTipoRequest();
        void setTipoRequest(int tipoRequest);
        void Dispose ();
    private:
        int sender;
        char mensaje[141];
        int tipoRequest;
};

#include "Mensaje.cxx"

#endif

Obeying to the definition of a POD-class:

A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). A POD class is a class that is either a POD struct or a POD union. What changes for C++11?: Definition of POD-class

Changing the I/O function calls as:

int pipeStatus = write (fdSendingPipe, mensaje, sizeof (Mensaje));
int statusMessagePipe = read (fdMessagePipe, mensaje, sizeof (Mensaje));
Community
  • 1
  • 1
Nevinyrral
  • 17
  • 5