进程间通信-二进制数据/序列化对象 [英] Interprocess communication - Binary data/serialized objects

查看:104
本文介绍了进程间通信-二进制数据/序列化对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前在同一台计算机上运行两个进程,一个生产者"和一个负责显示数据的Qt应用程序.这两个需要交换二进制数据(字节数组,序列化对象).数据包裹"的大小范围可以从几个字节到几十MB.

一种简单/优雅的方法是什么?

我曾考虑过使用boost :: asio和Google协议缓冲区,共享内存区域或只是低级套接字来实现这一目标,但是我对学习其他可能被我忽略的解决方案很感兴趣.

性能并不是绝对关键,但是适当的延迟(<1秒)和带宽(例如,> 5MB/秒)是必需的.

提前谢谢!

解决方案

如果您不喜欢额外的库,则可以尝试以下操作:

#if defined _WIN32 || defined _WIN64
    #include <windows.h>
#else
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <dlfcn.h>
    #include <fcntl.h>
    #include <unistd.h>
#endif

#include <cstdint>


typedef struct
{
    #if defined _WIN32 || defined _WIN64
    void* hFileMap;
    #else
    int hFileMap;
    #endif
    void* pData;
    size_t size;
} MemoryMap;

void* CreateMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
    #if defined _WIN32 || defined _WIN64
    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, MapName)) == NULL)
    {
        return NULL;
    }

    if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
    {
        CloseHandle(info->hFileMap);
        return NULL;
    }

    #else

    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
    {
        return NULL;
    }

    if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
    {
        close(hFileMap);
        return NULL;
    }
    #endif

    info->size = size;
    return info->pData;
}

void* OpenMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
    #if defined _WIN32 || defined _WIN64
    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, MapName)) == NULL)
    {
        return NULL;
    }

    if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
    {
        CloseHandle(info->hFileMap);
        return NULL;
    }

    #else

    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
    {
        return NULL;
    }

    if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
    {
        close(info->hFileMap);
        return NULL;
    }
    #endif

    info->size = size;
    return info->pData;
}

void CloseMap(MemoryMap* data)
{
    #if defined _WIN32 || defined _WIN64
    UnmapViewOfFile(data->pData);
    CloseHandle(data->hFileMap);
    #else
    munmap(data->pData, data->size);
    close(data->hFileMap);
    #endif
}



template<typename T>
class CAllocator
{
    private:
        size_t size;
        void* data = nullptr;

    public:
        typedef T* pointer;
        typedef const T* const_pointer;

        typedef T& reference;
        typedef const T& const_reference;

        typedef size_t size_type;
        typedef ptrdiff_t difference_type;

        typedef T value_type;


        CAllocator() {}
        CAllocator(void* data_ptr, size_type max_size) noexcept : size(max_size), data(data_ptr) {};

        template<typename U>
        CAllocator(const CAllocator<U>& other) noexcept {};

        CAllocator(const CAllocator &other) : size(other.size), data(other.data) {}

        template<typename U>
        struct rebind {typedef CAllocator<U> other;};

        pointer allocate(size_type n, const void* hint = 0) {return static_cast<pointer>(data);}
        void deallocate(void* ptr, size_type n) {}
        size_type max_size() const {return size / sizeof(T);}
};

template <typename T, typename U>
inline bool operator == (const CAllocator<T>&, const CAllocator<U>&) {return true;}

template <typename T, typename U>
inline bool operator != (const CAllocator<T>& a, const CAllocator<U>& b) {return !(a == b);}



/** Test case **/
#include <vector>
#include <iostream>
#include <stdexcept>

int main()
{
    /** Sender **/
    MemoryMap data = {0};
    if (!CreateMemoryMap(&data, "MapName", 1024))
    {
        if (!OpenMemoryMap(&data, "MapName", 1024))
        {
            throw std::runtime_error("Cannot map memory.");
        }
    }

    std::vector<int, CAllocator<int>> shared_sender_vector(CAllocator<int>(data.pData, data.size));
    shared_sender_vector.push_back(10);

    for (int i = 0; i < 10; ++i)
    {
        shared_sender_vector.push_back(i + 1);
    }



    /** Receiver **/


    MemoryMap data2 = {0};
    if (!CreateMemoryMap(&data2, "MapName", 1024))
    {
        if (!OpenMemoryMap(&data2, "MapName", 1024))
        {
            throw std::runtime_error("Cannot map memory.");
        }
    }


    int* offset = static_cast<int*>(data2.pData);
    std::vector<int, CAllocator<int>> shared_receiver_vector(CAllocator<int>(++offset, data2.size));
    shared_receiver_vector.reserve(*(--offset));

    for (int i = 0; i < 10; ++i)
    {
        std::cout<<shared_receiver_vector[i]<<" ";
    }

    CloseMap(&data);
    CloseMap(&data2);
}

它打印:

1 2 3 4 5 6 7 8 9 10

可用于字符串和接受分配器的各种容器.他们将数据直接存储在共享内存映射中.分配器与我为之分配的分配器相同:

需要提升池经验. 可以用作具有预分配功能的分配器吗?

I currently have two processes running on the same computer, a 'producer' and a Qt application tasked with displaying the data. These two need to exchange binary data (byte arrays, serialized objects). The data 'parcels' may range in size from a handful of bytes to tens of MBs.

What's a/the simple and elegant way to do this?

I thought about using boost::asio and Google Protocol Buffers, shared memory regions or just low-level sockets to achieve this, but I'm interested in learning about other solutions that I may have overlooked.

Performance is not absolutely critical but decent latency (<1 sec) and bandwidth (let's say, >5MB/sec) are necessary.

Thanks in advance!

解决方案

If you don't like extra libraries then you can try the following:

#if defined _WIN32 || defined _WIN64
    #include <windows.h>
#else
    #include <sys/types.h>
    #include <sys/mman.h>
    #include <dlfcn.h>
    #include <fcntl.h>
    #include <unistd.h>
#endif

#include <cstdint>


typedef struct
{
    #if defined _WIN32 || defined _WIN64
    void* hFileMap;
    #else
    int hFileMap;
    #endif
    void* pData;
    size_t size;
} MemoryMap;

void* CreateMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
    #if defined _WIN32 || defined _WIN64
    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, MapName)) == NULL)
    {
        return NULL;
    }

    if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
    {
        CloseHandle(info->hFileMap);
        return NULL;
    }

    #else

    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
    {
        return NULL;
    }

    if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
    {
        close(hFileMap);
        return NULL;
    }
    #endif

    info->size = size;
    return info->pData;
}

void* OpenMemoryMap(MemoryMap* info, const char* MapName, unsigned int size)
{
    #if defined _WIN32 || defined _WIN64
    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, MapName)) == NULL)
    {
        return NULL;
    }

    if ((info->pData = MapViewOfFile(info->hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, size)) == NULL)
    {
        CloseHandle(info->hFileMap);
        return NULL;
    }

    #else

    info->hFileMap = NULL;
    info->pData = NULL;
    info->size = 0;

    if ((info->hFileMap = open(MapName, O_RDWR | O_CREAT, 438)) == -1)
    {
        return NULL;
    }

    if ((info->pData = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, hFileMap, 0)) == MAP_FAILED)
    {
        close(info->hFileMap);
        return NULL;
    }
    #endif

    info->size = size;
    return info->pData;
}

void CloseMap(MemoryMap* data)
{
    #if defined _WIN32 || defined _WIN64
    UnmapViewOfFile(data->pData);
    CloseHandle(data->hFileMap);
    #else
    munmap(data->pData, data->size);
    close(data->hFileMap);
    #endif
}



template<typename T>
class CAllocator
{
    private:
        size_t size;
        void* data = nullptr;

    public:
        typedef T* pointer;
        typedef const T* const_pointer;

        typedef T& reference;
        typedef const T& const_reference;

        typedef size_t size_type;
        typedef ptrdiff_t difference_type;

        typedef T value_type;


        CAllocator() {}
        CAllocator(void* data_ptr, size_type max_size) noexcept : size(max_size), data(data_ptr) {};

        template<typename U>
        CAllocator(const CAllocator<U>& other) noexcept {};

        CAllocator(const CAllocator &other) : size(other.size), data(other.data) {}

        template<typename U>
        struct rebind {typedef CAllocator<U> other;};

        pointer allocate(size_type n, const void* hint = 0) {return static_cast<pointer>(data);}
        void deallocate(void* ptr, size_type n) {}
        size_type max_size() const {return size / sizeof(T);}
};

template <typename T, typename U>
inline bool operator == (const CAllocator<T>&, const CAllocator<U>&) {return true;}

template <typename T, typename U>
inline bool operator != (const CAllocator<T>& a, const CAllocator<U>& b) {return !(a == b);}



/** Test case **/
#include <vector>
#include <iostream>
#include <stdexcept>

int main()
{
    /** Sender **/
    MemoryMap data = {0};
    if (!CreateMemoryMap(&data, "MapName", 1024))
    {
        if (!OpenMemoryMap(&data, "MapName", 1024))
        {
            throw std::runtime_error("Cannot map memory.");
        }
    }

    std::vector<int, CAllocator<int>> shared_sender_vector(CAllocator<int>(data.pData, data.size));
    shared_sender_vector.push_back(10);

    for (int i = 0; i < 10; ++i)
    {
        shared_sender_vector.push_back(i + 1);
    }



    /** Receiver **/


    MemoryMap data2 = {0};
    if (!CreateMemoryMap(&data2, "MapName", 1024))
    {
        if (!OpenMemoryMap(&data2, "MapName", 1024))
        {
            throw std::runtime_error("Cannot map memory.");
        }
    }


    int* offset = static_cast<int*>(data2.pData);
    std::vector<int, CAllocator<int>> shared_receiver_vector(CAllocator<int>(++offset, data2.size));
    shared_receiver_vector.reserve(*(--offset));

    for (int i = 0; i < 10; ++i)
    {
        std::cout<<shared_receiver_vector[i]<<" ";
    }

    CloseMap(&data);
    CloseMap(&data2);
}

It prints:

1 2 3 4 5 6 7 8 9 10

Can be used for strings and all sorts of containers that accepts allocators. They store their data directly in the shared-memory map. The allocator is the same as the one I wrote for:

Boost Pool experience requested. Is it useful as allocator with preallocation? so it can be reused for whatever you want..

这篇关于进程间通信-二进制数据/序列化对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆