namespace FbMsg;

enum MsgType: uint
{   
    Unknown = 0,
    LoginReqType = 101,
    LoginRspType = 102,
    EntrustType = 103,
    EntrustRtnType = 104,
    TradeRtnType = 105,
    QryFundAccType = 106,
    QryFundAccRspType = 107,
    QryOrdersType = 108,
    QryOrdersRspType = 109,
    QryTradesType = 110,
    QryTradesRspType = 111,
    QryPositionsType = 112,
    QryPositionsRspType = 113,
    CancelOrderType = 114,
    CancelOrderRtnType = 115,
    ErrorInfoType = 116,
    QryShareHolderType = 117,
    QryShareHolderRspType = 118,
    HeartBeatType = 119,
    HeartBeatTypeRspType = 120,
    AgentRegisterType = 121,
    AgentRegisterRspType = 122
}
table Header
{
    Msgtype: MsgType;
    MsgLength: uint;
    ChannelId: uint;
    AccountId: uint;
    ClientId: uint;
}

table LoginReq
{
    ChannelId: string;
    AccountId: string;
    Password: string;
}

table LoginRsp
{
    ChannelId: string;
    AccountId: string;
    TradingDay: string;
    LoginTime: string;
    MaxLocalId: uint32;
}

table Order
{
    ChannelId: string;
    ExchangeId: string;
    InstrumentId: string;
    AccountId: string;
    OrderSysId: string;
    Volume: long;
    VolumeLeft: long;
    VolumeTraded: long;
    LimitPrice: double;
    TradeAmount: double;
    BuyOrSell: byte;
    EnumBuySell: byte;
    HedgeFlag: byte;
    OrderPriceType: byte;
    OpenOrClose: byte;
    OrderStatus: byte;
    InsertTime: string;
    LocalInsertTime: string;
    OrderId: string;
    OrigOrderSysId: string;
    OrigOrderId: string;
    StatusMsg: string;
    Trader: string;
}

table Trade
{
    ChannelId: string;
    ExchangeId: string;
    InstrumentId: string;
    AccountId: string;
    OrderSysId: string;
    OrderId: string;
    TradeId: string;
    Price: double;
    Volume: long;
    TradeTime: string;
    BuyOrSell: byte;
    HedgeFlag: byte;
    OpenOrClose: byte;
    UpdateTime: string;
    Trader: string;
}

table FundAccount
{
    ChannelId: string;
    AccountId: string;
    Available: double;
    Balance: double;
    UpdateTime: string;
}

table Position
{
    ChannelId: string;
    AccountId: string;
    ExchangeId: string;
    InstrumentId: string;
    Name: string;
    Volume: long;
    EnableVolume: long;
    YdVolume: long;
    TdVolume: long;
    AvgPrice: double;
    BuyOrSell: byte;
    HedgeFlag: byte;
    UpdateTime: string;
}

table ErrorInfo
{
    ChannelId: string;
    AccountId: string;
    ErrorId: uint;
    ErrorMsg: string;
    UpdateTime: string;
}

table QryReq
{
    ChannelId: string;
    AccountId: string;
}

table ShareHolder
{
    ChannelId: string;
    AccountId: string;
    ExchangeId: string;
    ShareHolderId: string;
    ShareHolderName: string;
    UpdateTime: string;
}

table HeartBeat
{
    ChannelId: string;
    AccountId: string;
    LastTime: string;
    ErrorId: uint;
    ErrorMsg: string;
}


//伪代码

// 发送（消息发送序列）
// 1.target_id
// 2.msg(header+body)

// 接收（消息接收序列）
// 1.msg(hear+body)

class PackMsg
{
public:
    PackMsg()
    :m_msgLength(0)
    {
        m_buffer = new uchar_t[10240];
    }

    ~PackMsg()
    {
        if(m_buffer)
            delete m_buffer;
    }

    void PackMsg::pack(Header *header, uint8_t *msg, int msg_size)
    {
        std::memcpy(m_buffer, header, sizeof(Header));
        std::memcpy(m_buffer+sizeof(Header), msg, msg_size);
        m_msgLength = msg_size + sizeof(Header);
    }

    void PackMsg::pack(Header *header)
    {
        std::memcpy(m_buffer, header, sizeof(Header));
        m_msgLength = sizeof(Header);
    }

    uchar_t *PackMsg::getPackMsg()
    {
        return m_buffer;
    }

    uint32_t PackMsg::getPackMsgLength()
    {
        return m_msgLength;
    }
private:
    private:
    uchar_t*    m_buffer;
    uint32_t    m_msgLength;
}


// 发送
// 1.target_id
// 2.msg(header+body)
void insertOrder(Order& order)
{
    FbMsg::OrderT orderT;
    orderT.ChannelId = order.ChannelId;
    orderT.ExchangeId = order.ExchangeId;
    // ......
    flatbuffers::FlatBufferBuilder builder(1024);
    auto fbOrder = FbMsg::CreateOrder(builder,&orderT);
    builder.Finish(fbOrder);

    Header header;
    header.MsgLength = sizeof(Header) + builder.GetSize();
    header.MsgType = MsgType::EntrustType;
    header.ChannelId = 101;
    PackMsg msg;
    msg.pack(&header, builder.GetBufferPointer(), builder.GetSize());

    std::lock_guard<std::mutex> lock(m_mtx);
    std::string target_id = "recv";
    target_id = order.ChannelId + order.AccountId + target_id;
    ZmqHelper::sendBuffMsg(m_pReqSocket, (uchar_t*)target_id.c_str(), target_id.length(), ZMQ_SNDMORE);
    ZmqHelper::sendPackMsg(m_pReqSocket, &msg);
}


// 接收
// 1.msg(hear+body)
void Agent::listenMessages()
{
    const int BUFFSIZE = 1024;
    uchar_t buff[BUFFSIZE] = {0};

    while(true)
    {
        zmq_pollitem_t items[] = {{ m_pRspSocket, 0, ZMQ_POLLIN, 0 }};
        int rc = zmq_poll(items, 1, -1);
        if (rc == -1) {
            Logger::getInstance().log->error("Agent::listenMessages Poll error:{}",zmq_strerror(errno)); 
            break;
        }

        if(ZmqHelper::recvPackMsg(m_pRspSocket,buff)==-1)
        {
            continue;
        }

        Header header;
        memcpy(&header,buff,sizeof(Header));
        if(header.MsgType==MsgType::LoginReqType)
        {
            auto loginReq = flatbuffers::GetRoot<FbMsg::LoginReq>(buff+sizeof(Header));
            FbMsg::LoginReqT* loginReqT = loginReq->UnPack();
            handleLogin(header, loginReqT);
        }
        else if(header.MsgType==MsgType::EntrustType)
        {
            auto order = flatbuffers::GetRoot<FbMsg::Order>(buff+sizeof(Header));
            FbMsg::OrderT* orderT = order->UnPack();
            handleEntrust(header, orderT);
        }
        // ......
        memset(buff,0,sizeof(buff));
    }
    zmq_close (m_pRspSocket);
    zmq_ctx_destroy (m_pRspContext);
}
结合上面C++ 代码，帮我写一个使用C# 发送心跳包和登录返回的方法