c++ - 如何从消息队列接收动态长度数据?

标签 c++ malloc message-queue

我必须使用 SysV 消息队列为大学项目发送和接收动态数据。

数据的长度在单独的消息中传输,size因此是已知的。

这就是我尝试接收数据的方式。我必须承认我不是 C++ 专家,尤其是在内存分配方面。

<罢工>

<罢工>
struct {
    long mtype;
    char *mdata;
} msg;

msg.mdata = (char *)malloc(size * sizeof(char));

msgrcv(MSGQ_ID, &msg, size, MSG_ID, 0);

问题好像是malloc打电话,但我不知道该怎么做。

编辑

我尝试的是在消息队列周围的 OO 包装器中使用某种读取 方法。我想将消息队列中的数据读入 char[]std::string .我现在所拥有的(简化)如下所示。

bool Wrapper::read(char *data, int length)
{
    struct Message {
        long mtype;
        std::string mdata;
    };

    Message msg;
    msg.mdata = std::string(size, '\0');

    if(msgrcv(MSGQ_ID, &msg, size, MSG_ID, 0) < 0)
    {
        return false;
    }

    memcpy(data, msg.mdata.c_str(), msg.mdata.size());

    return true;
}

我得到的只是段错误或完全损坏的数据(尽管这些数据有时包含我想要的内容)。

最佳答案

您不能将指向包含 std::string 成员的结构的指针传递给 msgrcv,这违反了接口(interface)协定。

传递给 msgrcv 的第二个参数需要指向一个缓冲区,该缓冲区具有足够的空间来存储 struct { long mtype; 形式的“普通”C 结构;字符 mdata[大小]; }; 其中 size 是 msgrcv 的第三个参数。

不幸的是,由于可能的对齐问题,确定此缓冲区的大小可能取决于 size,但您必须假设它不在提供此类接口(interface)的系统上。您可以使用标准的 offsetof 宏来帮助确定此大小。

由于 vector 连续存储其组件,一旦知道缓冲区的大小,就可以调整 charvector 的大小并使用这个用来保存缓冲区。使用 vector 可以免除您手动释放删除[]缓冲区的义务。

你需要做这样的事情。

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<char> 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);
}

反之,您只需按照 msgsnd 接口(interface)的要求将数据打包到一个 struct hack 兼容数据数组中。正如其他人指出的那样,这不是一个好的接口(interface),但掩盖了实现定义的行为和对齐问题,这样的事情应该可行。

例如

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<char> msgbuf(data.size() + data_offset);

    long mtype = MSG_ID;
    const char* mtypeptr = reinterpret_cast<char*>(&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();
    }
}

关于c++ - 如何从消息队列接收动态长度数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1181816/

相关文章:

c++ - 为什么在使用我的 auto_ptr 样式类访问结构成员时得到 'unidentified identifier'?

c++ - 从C++中的变量打印文件路径

c++ - 通过引用传递的转换整数类型

c - 在 C 头文件中声明的结构 - gcc malloc 错误

java - 在高性能 Java 应用程序中异步处理低速消费者(数据库)的最佳方式是什么

python - celery 任务消失 - Django/Celery

c++ - QTabWidget设置只有一个tab的标题字体

c - 内存泄漏双指针

c++ - cudaMalloc()是否将数组初始化为0?

java - 以静默方式将作业返回到队列