我通过命名管道传输了一个 std::pair 它是如何工作的?

Posted

技术标签:

【中文标题】我通过命名管道传输了一个 std::pair 它是如何工作的?【英文标题】:I transferred a std::pair over a named pipe how did it work? 【发布时间】:2017-04-20 17:09:14 【问题描述】:

我在 Linux 中使用 read()write() 函数通过命名管道传递了 std::pair。我通过引用传递它,并通过引用阅读它。那是怎么工作的?据我所知,std::pair 没有序列化,当引用超出范围时会发生什么。可以移动对象还是通过引用复制?

我很困惑。

我是 C++ 新手,所以我的指针和引用知识有点差,但据我所知,我通过管道传递地址,然后将读取端对象指向这个新地址。

虽然读取端对象是一个对象,而不是指针。那么它的旧/原始实例会发生什么?

我正在使用此代码编写:

std::pair<int, somestruct> sample_pair;
write(FD, &sample_pair, sizeof(sample_pair));

并使用以下代码阅读它:

std::pair<int, somestruct> sample_pair;
read(FD, &sample_pair, sizeof(sample_pair));

【问题讨论】:

【参考方案1】:

您根本没有通过管道发送std::pair地址。您正在发送std::pair 的实际内容

您将std::pair 实例的地址作为起始地址传递给write(),告诉它读取从该初始地址开始的sizeof(sample_pair) 字节数并将它们顺序写入管道。因此,存储在std::pair 中的原始字节按原样写入管道。

您将std::pair 实例的地址作为起始地址传递给read(),告诉它从管道中读取sizeof(sample_pair) 字节数并将它们按顺序保存到该初始地址。因此,从管道接收的原始字节按原样存储在 std::pair 内。

话虽如此,以这种方式发送std::pair 仍然是未定义的行为,因为std::pair 的实际内存布局是特定于实现的,特别是在两个字段之间可能存在的任何对齐填充方面。您需要手动将数据序列化为跨进程边界安全的格式,然后在另一端反序列化。

例如,它可能就像单独发送两个字段一样简单(假设somestruct 是自包含的(没有指向外部数据的指针)并且没有自己的对齐问题),例如:

#pragma pack(push, 1)
struct somestruct

    int8_t value1;
    int16_t value2;
    int32_t value3;
    char data[256];
;
#pragma pack(pop)

std::pair<int32_t, somestruct> sample_pair;
// populate as needed...

write(FD, &(sample_pair.first), sizeof(sample_pair.first));
write(FD, &(sample_pair.second), sizeof(sample_pair.second));

std::pair<int32_t, somestruct> sample_pair;

read(FD, &(sample_pair.first), sizeof(sample_pair.first));
read(FD, &(sample_pair.second), sizeof(sample_pair.second));

或者,它可能很复杂,必须在发送之前将somestruct 的内容扁平化为一个连续的byte[] 数组,例如:

struct somestruct

    int8_t value1;
    int16_t value2;
    int32_t value3;
    std::string data;
;

std::pair<int32_t, somestruct> sample_pair;
// populate as needed...

std::vector<uint8_t> buffer(
  sizeof(int32_t) +
  sizeof(int8_t) +
  sizeof(int16_t) +
  sizeof(int32_t) +
  sizeof(int32_t) +
  sample_pair.second.data.length()
);

uint8_t *ptr = &buffer[0];

*reinterpret_cast<int32_t*>(ptr) = htonl(sample_pair.first);
ptr += sizeof(int32_t);

*reinterpret_cast<int8_t*>(ptr) = sample_pair.second.value1;
ptr += sizeof(int8_t);

*reinterpret_cast<int16_t*>(ptr) = htons(sample_pair.second.value2);
ptr += sizeof(int16_t);

*reinterpret_cast<int32_t*>(ptr) = htonl(sample_pair.second.value3);
ptr += sizeof(int32_t);

*reinterpret_cast<int32_t*>(ptr) = htonl(sample_pair.second.data.length());
ptr += sizeof(int32_t);

std::copy(sample_pair.second.data.cbegin(), sample_pair.second.data.cend(), reinterpret_cast<char*>(ptr));

int32_t len = htonl(buffer.size());
write(FD, &len, sizeof(len));
write(FD, buffer.data(), buffer.size());

std::pair<int32_t, somestruct> sample_pair;

int32_t len;
read(FD, &len, sizeof(len));
len = ntohl(len);

std::vector<uint8_t> buffer(len);
uint8_t *ptr = &buffer[0];

read(FD, ptr, len);

sample_pair.first = ntohl(*reinterpret_cast<int32_t*>(ptr));
ptr += sizeof(int32_t);

sample_pair.second.value1 = *reinterpret_cast<int8_t*>(ptr);
ptr += sizeof(int8_t);

sample_pair.second.value2 = ntohs(*reinterpret_cast<int16_t*>(ptr));
ptr += sizeof(int16_t);

sample_pair.second.value3 = ntohl(*reinterpret_cast<int32_t*>(ptr));
ptr += sizeof(int32_t);

len = ntohl(*reinterpret_cast<int32_t*>(ptr));
ptr += sizeof(int32_t);

sample_pair.second.data.assign(reinterpret_cast<char*>(ptr), len);

或者,您可以单独发送值:

void sendInt8(int FD, int8_t value)

    write(FD, &value, sizeof(value));


void sendInt16(int FD, int16_t value)

    value = htons(value);
    write(FD, &value, sizeof(value));


void sendInt32(int FD, int32_t value)

    value = htonl(value);
    write(FD, &value, sizeof(value));


void sendStr(int FD, const std::string &value)

    sendInt32(FD, value.length());
    write(FD, value.c_str(), value.length());


...

std::pair<int32_t, somestruct> sample_pair;
// populate as needed...

sendInt32(FD, sample_pair.first);
sendInt8(FD, sample_pair.second.value1);
sendInt16(FD, sample_pair.second.value2);
sendInt32(FD, sample_pair.second.value3);
sendStr(FD, sample_pair.second.data);

int8_t readInt8(int FD)

    int8_t value;
    read(FD, &value, sizeof(value));
    return value;


int16_t readInt16(int FD)

    int16_t value;
    read(FD, &value, sizeof(value));
    return ntohs(value);


int32_t readInt16(int FD)

    int32_t value;
    read(FD, &value, sizeof(value));
    return ntohl(value);


std::string readStr(int FD)

    std::string value;
    int32_t len = readInt32(FD);
    if (len > 0)
    
        value.resize(len);
        read(FD, &value[0], len);
    
    return value;


...

std::pair<int32_t, somestruct> sample_pair;

sample_pair.first = readInt32(FD);
sample_pair.second.value1 = readInt8(FD);
sample_pair.second.value2 = readInt16(FD);
sample_pair.second.value3 = readInt32(FD);
sample_pair.second.data = readStr(FD);

【讨论】:

以上是关于我通过命名管道传输了一个 std::pair 它是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

双向命名管道问题

如何通过命名管道传输文件描述符

将 ffmpeg 输出通过管道传输到命名管道

返回 std::pair 与通过非常量引用传递

OpenCV Python,从命名管道中读取视频

windows命名管道