boost::interprocess::message_queue 第二个进程没有收到消息

boost::interprocess::message_queue no message received in second process

我正在使用 boost 的消息队列编写一个只有两个字符数组的基本 class,但是在第二个进程中没有接收到的数据是空的,即使 get_num_msg() returns 读取前为 1,读取后为 returns 0。出于调试目的,我还尝试从同一进程写入和读取,并且效果很好。我正在使用共享指针,因为早些时候在读写整数时,除非它被声明为共享指针,否则它不会在接收方读取整数。

访问队列

class AccessQueue {
    public:
    char name[64];
    char action[64];
    AccessQueue(char name[64], char action[64]) {
        strcpy(this->name, name);
        strcpy(this->action, action);
    }
    AccessQueue() {}
};

发送函数

// std::shared_ptr<AccessQueue> p1;
this->p1.reset(new AccessQueue("asd", "vsq"));
try {
    this->mq->send(&p1, sizeof(p1), 0);
} catch(boost::interprocess::interprocess_exception & ex) {
    std::cout << ex.what() << std::endl;
}

接收函数

std::cout << this->mq->get_num_msg() << "\t" << this->mq->get_max_msg_size() << "\t" << this->mq->get_max_msg() << std::endl;
AccessQueue * a;
unsigned int priority;
boost::interprocess::message_queue::size_type recvd_size;
try {
    this->mq->try_receive(&a, sizeof(AccessQueue), recvd_size, priority);
} catch(boost::interprocess::interprocess_exception & ex) {
    std::cout << ex.what() << std::endl;
}
std::cout << this->mq->get_num_msg() << "\t" << this->mq->get_max_msg_size() << "\t" << this->mq->get_max_msg() << std::endl;
std::cout << "It clearly maybe works " << a->action << "\t" << a->name << std::endl;

接收端输出:

1       128     20
0       128     20

看起来 p1(在发送函数中)是一个智能指针(如 std::unique_ptrstd::shared_ptr)。那样的话

this->mq->send(&p1, sizeof(p1), 0);

显然是错误的,因为它把指针对象放到了队列中,而不是数据结构。使用

this->mq->send(*p1, sizeof(*p1), 0);

或者,实际上,首先不要使用动态分配:

AccessQueue packet("asd", "vsq");
mq.send(&packet, sizeof(packet), 0);

呃还有更多

接收方也有类似的问题:

    AccessQueue * a;
    // ..
    mq.try_receive(&a, sizeof(AccessQueue), ...);

接收INTO的是指针,不是对象。您甚至没有对象,因为 a (指针)从未被初始化。这里的修复在语法上很简单:

    AccessQueue a;

没有更多的指针。现在,a 是一个对象,&a 是该对象的地址。

Note how the original was UB because you read sizeof(AccessQueue) bytes into a pointer. However the pointer is only 8 bytes and the struct is 128 bytes. Ooops!

简化的工作演示

这个有效:

Live On Wandbox¹

#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream>
#include <iomanip>

namespace bip = boost::interprocess;
using MQ = bip::message_queue;

template<size_t N>
static inline void safe_copy(std::array<char, N>& dst, std::string_view src) {
    std::copy_n(src.data(), std::min(src.size(), N), dst.data());
    dst.back() = 0; // make sure of NUL termination
}

struct AccessQueue {
    std::array<char, 64> name{0};
    std::array<char, 64> action{0};

    AccessQueue(std::string_view n = "", std::string_view a = "") {
        safe_copy(name, n);
        safe_copy(action, a);
    }
};

static_assert(std::is_standard_layout_v<AccessQueue>);

struct X {
    void send() {
        AccessQueue packet("asd", "vsq");
        try {
            mq.send(&packet, sizeof(packet), 0);
        } catch(std::exception const & ex) {
            std::cout << ex.what() << std::endl;
        }
    }

    AccessQueue receive() {
        AccessQueue retval;
        
        report();
        try {
            unsigned int priority;
            MQ::size_type recvd_size;
            mq.try_receive(&retval, sizeof(AccessQueue), recvd_size, priority);
        } catch(std::exception const & ex) {
            std::cout << ex.what() << std::endl;
        }
        report();
        return retval;
    }

    void report() {
        std::cout << mq.get_num_msg() << "\t" << mq.get_max_msg_size() << "\t" << mq.get_max_msg() << std::endl;
    }

    MQ mq { bip::open_or_create, "somequeue", 10, sizeof(AccessQueue) };
};

int main() {
    X tryit;
    tryit.send();
    auto const& [name, action] = tryit.receive();

    std::cout << std::quoted(name.data()) << " " << std::quoted(action.data()) << std::endl;
}

版画

1       128     10
0       128     10
"asd" "vsq"

备注

  • 在 C 数组上使用 std::array 默认情况下为您提供复制语义
  • 保护 AccessQueue 的 POD-ness
  • 确保成员已初始化
  • 确保副本安全
  • 确保副本始终以 NUL 结尾
  • 不要使用 new 或删除。 Why should C++ programmers minimize use of 'new'?
  • 确保您的接收缓冲区大小与 max_msg_size ()
  • 匹配

¹ Wandbox 禁止共享内存:(