windows下FileMapping封装,支持双端通信

FileMapping.h

/*****************************************************************
*
* @brief:   Wrapper for file mapping on Windows, duplex process communication support
* @auth:    1058778041@qq.com
* @date:    2025/04/18
* @update:
*
******************************************************************/
#pragma once
#include <string>
#include <functional>
#include "Utils/ThreadObject.h"



using FuncDataCallback = std::function<void(const std::string& strMsg)>;
namespace Utils {
class FileMapping
{
    const std::string kGlobalTag = "Global\\";
    const std::string kDuplexTag = "Duplex";
    const std::string kSemaphoreTag = "Sema";
    const std::string kEventTag = "Evt";
public:
    FileMapping();
    /*
    *
    * @brief:       Create mapping in server
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @para[in]:    strMapping, name for file mapping
    * @para[in]:    bGlobal, global tag; set true if system process for server
    * @para[in]:    bDuplex, duplex communication; set true if client sends msg to server
    * @return:      bool, true: success, or failed
    *
    */
    bool CreateMapping(const std::string& strMapping, bool bGlobal = false, bool bDuplex = false);
    /*
    *
    * @brief:       Connect mapping in client
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @para[in]:    strMapping, name for file mapping
    * @para[in]:    bGlobal, global tag; set true if system process for server
    * @para[in]:    bDuplex, duplex communication tag; set true if client sends msg to server
    * @return:      bool, true: success, or failed
    *
    */
    bool ConnectServer(const std::string& strMapping, bool bGlobal = false, bool bDuplex = false);
    /*
    *
    * @brief:       Write data to client
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @para[in]:    strData, msg to write
    * @return:      bool, true: success, or failed
    *
    */
    bool WriteDataToClient(const std::string& strData);
    /*
    *
    * @brief:       Write data to server
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @para[in]:    strData, msg to write
    * @para[in]:    nTimeout, timeout to wait write
    * @return:      bool, true: success, or failed
    *
    */
    bool WriteDataToServer(const std::string& strData, int nTimeout = 30000);
    /*
    *
    * @brief:       listen event for msg(client and server)
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @para[in]:    cbData, callback while msg ready
    * @para[in]:    bServer, server tag; set true if duplex server (in server)
    * @return:      bool, true: success, or failed
    *
    */
    void ListenEvent(FuncDataCallback cbData, bool bServer = false);
    /*
    *
    * @brief:       close mapping
    * @auth:        1058778041@qq.com
    * @create:      2025/04/18
    * @return:      void
    *
    */
    void CloseMapping();

private:
    std::string handle_global_name(const std::string& strInput);
    void listen_event(bool bServer = false);
    bool check_stop();
    bool setup_mapping(const std::string& strName, bool bGlobal = false, bool bDuplex = false);
    bool open_mapping(const std::string& strName, bool bGlobal = false, bool bDuplex = false);

    bool setup_event(const std::string& strName, bool bGlobal = false, bool bDuplex = false);
    bool open_event(const std::string& strName, bool bGlobal = false, bool bDuplex = false);
    void notify_event(bool bReader = false);

    bool create_semaphore(const std::string& strSemaphore, bool bGlobal = false);
    bool open_semaphor(const std::string& strName, bool bGlobal = false);
    void release_semaphor();

    bool read_data(std::string& strData, bool bServer = false);
    bool write_data(const std::string& strData, bool bToServer = false, int nTimeout = 30000);


private:
    std::atomic<bool> m_bStop;
    FuncDataCallback m_cbMsg;
    // simple write(server) -> read(client)
    void* m_pMappingFile;
    void* m_pMappingData;
    void* m_pEventHandle;
    char* m_pFileData;

    // duplex write(client) -> read(server)
    bool m_bDuplex;
    void* m_pDuplexMapping;
    void* m_pDuplexReaderMapping;
    void* m_pDuplexEvent;
    char* m_pDuplexFileData;

    // semaphore
    void* m_pSemaphore;

    // work thread
    Utils::ThreadObject m_objWorker;

};
}

FileMapping.cpp

#include "FileMapping.h"
#include <Windows.h>
#include <sddl.h>
#include "Utils/Logger.h"


using namespace Utils;
FileMapping::FileMapping()
    : m_pMappingFile(nullptr)
    , m_pMappingData(nullptr)
    , m_pFileData(nullptr)
    , m_bStop(false)
    , m_pDuplexMapping(nullptr)
    , m_pDuplexReaderMapping(nullptr)
    , m_pDuplexFileData(nullptr)
    , m_pDuplexEvent(nullptr)
    , m_pSemaphore(nullptr)
    , m_bDuplex(false)
{

}

bool FileMapping::CreateMapping(const std::string& strMapping,
    bool bGlobal/* = false */,
    bool bDuplex/* = false */)
{
    m_bDuplex = bDuplex;
    bool bSucc = false;
    // create/open mapping
    if (!setup_mapping(strMapping, bGlobal)) return bSucc;
    if (!open_mapping(strMapping, bGlobal)) return bSucc;
    if (bDuplex) {
        std::string strDuplex = strMapping + kDuplexTag;
        if (!setup_mapping(strDuplex, bGlobal, bDuplex)) return bSucc;
        if (!open_mapping(strDuplex, bGlobal, bDuplex)) return bSucc;
    }

    // event
    std::string strEventName = strMapping + kEventTag;
    if(!setup_event(strEventName, bGlobal))  return bSucc;
    if (bDuplex) {
        std::string strDuplex = strEventName + kDuplexTag;
        if (!setup_event(strDuplex, bGlobal, bDuplex)) return bSucc;
    }

    // setup semaphore
    if (bDuplex) {
        std::string strSemaphor = strMapping + kSemaphoreTag;
        create_semaphore(strSemaphor, bGlobal);
    }

    return true;
}

bool FileMapping::ConnectServer(const std::string& strMapping,
    bool bGlobal/* = false */,
    bool bDuplex/* = false */)
{
    bool bSucc = false;
    // mapping
    if (!open_mapping(strMapping, bGlobal)) return bSucc;
    if (bDuplex) {
        std::string strDuplex = strMapping + kDuplexTag;
        if (!open_mapping(strDuplex, bGlobal, bDuplex)) {
            return bSucc;
        }
    }

    // event
    std::string strEventName = strMapping + kEventTag;
    if (!open_event(strEventName, bGlobal)) return bSucc;
    if (bDuplex) {
        std::string strDuplex = strEventName + kDuplexTag;
        if (!open_event(strDuplex, bGlobal, bDuplex)) {
            return bSucc;
        }
    }

    // semaphore
    if (bDuplex) {
        std::string strSemaphore = strMapping + kSemaphoreTag;
        if (!open_semaphor(strSemaphore, bGlobal)) return bSucc;
    }

    return true;
}

bool FileMapping::WriteDataToClient(const std::string& strData)
{
    return write_data(strData);
}

bool FileMapping::WriteDataToServer(const std::string& strData, int nTimeout/* = 30000*/)
{
    return write_data(strData, true, nTimeout);
}

void FileMapping::ListenEvent(FuncDataCallback cbData, bool bServer/* = false */)
{
    if (m_pEventHandle) {
        m_cbMsg = cbData;
        m_objWorker.Start(std::bind(&FileMapping::listen_event, this, bServer), 10);
    }
}

void FileMapping::CloseMapping()
{
    m_bStop.store(true);
    // it may trigger error signal to client or server, so ignore it
    //m_objWorker.Stop();
    if (m_pFileData) {
        UnmapViewOfFile(m_pFileData);
        m_pFileData = NULL;
    }
    if (m_pMappingData) {
        CloseHandle(m_pMappingData);
        m_pMappingData = NULL;
    }
    if (m_pMappingFile) {
        CloseHandle(m_pMappingFile);
        m_pMappingFile = NULL;
    }
    if (m_pEventHandle) {
        CloseHandle(m_pEventHandle);
        m_pEventHandle = NULL;
    }
    // duplex
    if (m_pDuplexFileData) {
        UnmapViewOfFile(m_pDuplexFileData);
        m_pDuplexFileData = NULL;
    }
    if (m_pDuplexReaderMapping) {
        CloseHandle(m_pDuplexReaderMapping);
        m_pDuplexReaderMapping = NULL;
    }
    if (m_pDuplexMapping) {
        CloseHandle(m_pDuplexMapping);
        m_pDuplexMapping = NULL;
    }
    if (m_pDuplexEvent) {
        CloseHandle(m_pDuplexEvent);
        m_pDuplexEvent = NULL;
    }
    if (m_pSemaphore) {
        CloseHandle(m_pSemaphore);
        m_pSemaphore = NULL;
    }
}

std::string FileMapping::handle_global_name(const std::string& strInput)
{
    std::string strResult(strInput);
    if (strResult.find(kGlobalTag) == std::string::npos) {
        strResult = kGlobalTag + strResult;
    }

    return strResult;
}

void FileMapping::listen_event(bool bServer/* = false */)
{
    auto pEvent = bServer ? m_pDuplexEvent : m_pEventHandle;
    DWORD dwWaitResult = WaitForSingleObject(pEvent, INFINITE);
    if (dwWaitResult == WAIT_OBJECT_0) {
        if (check_stop()) {
            LOG(INFO) << "Receive exit signal";
            return;
        }
        if (m_cbMsg) {
            std::string strMsg;
            read_data(strMsg, bServer);
            m_cbMsg(strMsg);
        }
    }
}

bool FileMapping::check_stop()
{
    return m_bStop.load();
}

bool FileMapping::setup_mapping(const std::string& strName, bool bGlobal/* = false */, bool bReader/* = false*/)
{
    std::string strMappingName(strName);
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    // common user support
    PSECURITY_DESCRIPTOR pSD = NULL;
    if (bGlobal) {
        strMappingName = handle_global_name(strMappingName);
        if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
            L"D:(A;;GA;;;AU)(A;;GA;;;SY)",
            1,//SDDL_REVISION_1,
            &pSD,
            NULL)) {
        }
        sa.lpSecurityDescriptor = pSD;
    }
    HANDLE hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, 0x100, strMappingName.c_str());
    if (NULL == hFileMapping) {
        LOG(LERROR) << "Create file mapping error: " << GetLastError();
        return false;
    }
    if (bReader) {
        m_pDuplexMapping = (void*)hFileMapping;
    }
    else {
        m_pMappingFile = (void*)hFileMapping;
    }

    LOG(INFO) << "Create file mapping succ.";
    return true;
}

bool FileMapping::open_mapping(const std::string& strName, bool bGlobal/* = false */, bool bReader/* = false*/)
{
    std::string strMappingName(strName);
    if (bGlobal)
        strMappingName = handle_global_name(strMappingName);
    HANDLE hMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, strMappingName.c_str());
    if (NULL == hMapping) {
        LOG(LERROR) << "Open file mapping failed: " << GetLastError();
        return false;
    }
    char* pFileData = (char*)MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if (NULL == pFileData) {
        LOG(LERROR) << "Map view file failed: " << GetLastError();
        return false;
    }
    if (bReader) {
        m_pDuplexReaderMapping = (void*)hMapping;
        m_pDuplexFileData = pFileData;
    }
    else {
        m_pMappingData = (void*)hMapping;
        m_pFileData = pFileData;
    }
    return true;
}

bool FileMapping::setup_event(const std::string& strName, bool bGlobal/* = false */, bool bDuplex/* = false*/)
{
    std::string strEventName(strName);
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    // common user support
    PSECURITY_DESCRIPTOR pSD = NULL;
    if (bGlobal) {
        strEventName = handle_global_name(strEventName);
        if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
            L"D:(A;;GA;;;AU)",
            1,//SDDL_REVISION_1,
            &pSD,
            NULL)) {
        }
    }
    sa.lpSecurityDescriptor = pSD;
    HANDLE hEvent = CreateEventA(&sa, TRUE, FALSE, strEventName.c_str());
    if (NULL == hEvent) {
        LOG(LERROR) << "Create event failed: " << GetLastError();
        return false;
    }
    if (bDuplex) {
        m_pDuplexEvent = (void*)hEvent;
    }
    else {
        m_pEventHandle = (void*)hEvent;
    }

    return true;
}

bool FileMapping::open_event(const std::string& strName, bool bGlobal/* = false*/, bool bDuplex/* = false*/)
{
    std::string strEventName(strName);
    if (bGlobal)
        strEventName = handle_global_name(strEventName);
    HANDLE hEvent = OpenEventA(EVENT_ALL_ACCESS, FALSE, strEventName.c_str());
    if (INVALID_HANDLE_VALUE == hEvent) {
        LOG(LERROR) << "Open event failed: " << GetLastError();
        return false;
    }
    if (bDuplex) {
        m_pDuplexEvent = (void*)hEvent;
    }
    else {
        m_pEventHandle = (void*)hEvent;
    }

    return true;
}

void FileMapping::notify_event(bool bToServer/* = false*/)
{
    if (bToServer) {
        if (m_pDuplexEvent != NULL) {
            SetEvent(m_pDuplexEvent);
        }
    }
    else {
        if (m_pEventHandle != NULL) {
            SetEvent(m_pEventHandle);
            ResetEvent(m_pEventHandle);
        }
    }
}

bool FileMapping::create_semaphore(const std::string& strName, bool bGlobal/* = false*/)
{
    std::string strSemphorName(strName);
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;
    // common user support
    PSECURITY_DESCRIPTOR pSD = NULL;
    if (bGlobal) {
        strSemphorName = handle_global_name(strSemphorName);
        if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
            L"D:(A;;GA;;;AU)",
            1,//SDDL_REVISION_1,
            &pSD,
            NULL)) {
        }
    }
    sa.lpSecurityDescriptor = pSD;
    HANDLE hSemaphore = CreateSemaphoreA(&sa, 1, 1, strSemphorName.c_str());
    if (NULL == hSemaphore) {
        LOG(LERROR) << "Create semaphor error: " << GetLastError();
        return false;
    }
    m_pSemaphore = hSemaphore;
    LOG(INFO) << "Create semaphore succ";
    return true;
}

bool FileMapping::open_semaphor(const std::string& strName, bool bGlobal/* = false*/)
{
    std::string strSemaphorName(strName);
    if (bGlobal)
        strSemaphorName = handle_global_name(strSemaphorName);
    HANDLE hSemaphore = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, FALSE, strSemaphorName.c_str());
    if (NULL == hSemaphore) {
        LOG(LERROR) << "Open semaphore mapping failed: " << GetLastError();
        return false;
    }
    m_pSemaphore = hSemaphore;
    return true;
}

void FileMapping::release_semaphor()
{
    if(m_pSemaphore)
        ReleaseSemaphore(m_pSemaphore, 1, NULL);
}

bool FileMapping::read_data(std::string& strData, bool bServer/* = false */)
{
    bool bSucc = false;
    if (bServer) {
        if (NULL == m_pDuplexReaderMapping || NULL == m_pSemaphore) return bSucc;
        if (m_pDuplexFileData && strlen(m_pDuplexFileData)) {
            strData = m_pDuplexFileData;
            ResetEvent(m_pDuplexEvent);
            release_semaphor();
            return true;
        }
    }
    else {
        if (NULL == m_pMappingData) return bSucc;
        if (m_pFileData && strlen(m_pFileData)) {
            strData = m_pFileData;
            return true;
        }
    }

    return bSucc;
}

bool FileMapping::write_data(const std::string& strData, bool bToServer/* = false*/, int nTimeout/* = 30000*/)
{
    bool bSucc = false;
    if (bToServer) {
        if (NULL == m_pDuplexReaderMapping || NULL == m_pSemaphore) return bSucc;
        if (m_pDuplexFileData) {
            auto nCode = WaitForSingleObject(m_pSemaphore, nTimeout);
            if (WAIT_OBJECT_0 == nCode) {
                strcpy_s(m_pDuplexFileData, strData.length() + 1, strData.c_str());
#if 0
                {
                    LOG(INFO) << "Write data succ: " << strData;
                }
#endif
                notify_event(true);
                return true;
            }
            LOG(LERROR) << "Wait error to write: " << nCode;
        }
    }
    else {
        if (NULL == m_pMappingData) return bSucc;
        if (m_pFileData) {
            strcpy_s(m_pFileData, strData.length() + 1, strData.c_str());
            notify_event();
            return true;
        }
    }

    return true;
}


main.cpp

#include <iostream>
#include "Utils/FileMapping.h"
#include <thread>
#include <chrono>


std::string strMappingName("MappingTest");
bool bStop = false;
void testServer()
{
    Utils::FileMapping objMapping;
    bool bGlobal = false;
    bool bDuplex = true;
    objMapping.CreateMapping(strMappingName, bGlobal, bDuplex);

#if 0
    if (bDuplex) {
        objMapping.ListenEvent([=](const std::string& strMsg) {
            std::cout << "====Duplex====" << strMsg << "\n";
        }, true);
    }
#endif

    int nIndex = 0;
    while (!bStop) {
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));
        std::string strTestData = "Hello World " + std::to_string(++nIndex);
        objMapping.WriteDataToClient(strTestData);
    }
}

void testClient(const std::string strTag, bool bDuplex = false)
{
    Utils::FileMapping objMapping;
    bool bGlobal = false;
    objMapping.ConnectServer(strMappingName, bGlobal, bDuplex);

#if 1
    objMapping.ListenEvent([=](const std::string& strMsg) {
        std::cout << "[" << strTag << "]: " << strMsg << "\n";
    });
#endif

#if 0
    if (bDuplex) {
        int nIndex = 0;
        while (!bStop) {
            std::string strTestData = "[" + strTag + "] Msg: " + std::to_string(++nIndex);
            objMapping.WriteDataToServer(strTestData);
            std::this_thread::sleep_for(std::chrono::milliseconds(3000));
        }
    }
#endif

    getchar();
}

int main()
{
    std::thread objServer(testServer);
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    std::thread objClientx(testClient, "A", false);
    std::thread objClienta(testClient, "B", false);
    std::thread objClientC(testClient, "C", true);
    std::thread objClientD(testClient, "D", true);
#if 0
    std::thread objClientE(testClient, "E", false);
    std::thread objClientF(testClient, "F", false);
    std::thread objClientH(testClient, "G", false);
    std::thread objClientI(testClient, "H", false);
    std::thread objClientJ(testClient, "I", false);
    std::thread objClientK(testClient, "J", false);
#endif

    getchar();
    bStop = true;
    if (objServer.joinable()) objServer.join();
    //if (objClientx.joinable()) objClientx.join();
    //if (objClienta.joinable()) objClienta.join();
    return 0;
}



PS
1、支持服务->客户端(广播)
2、支持双端(客户端->服务器)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值