How to receive dynamic length data from a message queue?

前端 未结 5 721
陌清茗
陌清茗 2020-12-30 18:23

I have to send and receive dynamic data using a SysV message queue for a university project.

The length of the data is transmitted in a separate message, size<

5条回答
  •  南方客
    南方客 (楼主)
    2020-12-30 18:41

    You can't pass a pointer to a structure that contains a std::string member to msgrcv, this violates the interface contract.

    The second parameter passed to msgrcv needs to point to a buffer with sufficient space to store a 'plain' C struct of the form struct { long mtype; char mdata[size]; }; where size is the third parameter to msgrcv.

    Unfortunately, determining the size of this buffer might depend on size due to possible alignment issues but you have to assume that it doesn't on a system that provides this sort of interface. You can use the standard offsetof macro to help determine this size.

    As a vector stores its components contiguously, once you know the size of the buffer, you can resize a vector of char and use this to hold the buffer. Using a vector relieves you of the obligation to free or delete[] a buffer manually.

    You need to do something like this.

    std::string RecvMessage()
    {
        extern size_t size; // maximum size, should be a parameter??
        extern int MSGQ_ID; // message queue id, should be a parameter??
        extern long MSG_ID; // message type, should be a parameter??
    
        // ugly struct hack required by msgrcv
        struct RawMessage {
            long mtype;
            char mdata[1];
        };
    
        size_t data_offset = offsetof(RawMessage, mdata);
    
        // Allocate a buffer of the correct size for message
        std::vector msgbuf(size + data_offset);
    
        ssize_t bytes_read;
    
        // Read raw message
        if((bytes_read = msgrcv(MSGQ_ID, &msgbuf[0], size, MSG_ID, 0)) < 0)
        {
            throw MsgRecvFailedException();
        }
    
        // a string encapsulates the data and the size, why not just return one
        return std::string(msgbuf.begin() + data_offset, msgbuf.begin() + data_offset + bytes_read);
    }
    

    To go the other way, you just have to pack the data into a struct hack compatible data array as required by the msgsnd interface. As others have pointer out, it's not a good interface, but glossing over the implementation defined behaviour and alignment concerns, something like this should work.

    e.g.

    void SendMessage(const std::string& data)
    {
        extern int MSGQ_ID; // message queue id, should be a parameter??
        extern long MSG_ID; // message type, should be a parameter??
    
        // ugly struct hack required by msgsnd
        struct RawMessage {
            long mtype;
            char mdata[1];
        };
    
        size_t data_offset = offsetof(RawMessage, mdata);
    
        // Allocate a buffer of the required size for message
        std::vector msgbuf(data.size() + data_offset);
    
        long mtype = MSG_ID;
        const char* mtypeptr = reinterpret_cast(&mtype);
    
        std::copy(mtypeptr, mtypeptr + sizeof mtype, &msgbuf[0]);
        std::copy(data.begin(), data.end(), &msgbuf[data_offset]);
    
        int result = msgsnd(MSGQ_ID, &msgbuf[0], msgbuf.size(), 0);
        if (result != 0)
        {
            throw MsgSendFailedException();
        }
    }
    

提交回复
热议问题