C/C++ P2P自定义发现协议 Xndp。

Xndp.h

#pragma once

#include <frp/IDisposable.h>
#include <frp/threading/Hosting.h>
#include <frp/net/IPEndPoint.h>
#include <frp/configuration/MappingType.h>

namespace frp {
    namespace discover {
        /* X Network Discovery Protocol. */
        class Xndp : public IDisposable { /* Apply to P2P(peer-to-peer) is based on NAT/UDP put holes.*/
#pragma pack(push,1)
            struct XndpPacket {
                Byte                                                            kf;
                UInt16                                                          len; /* Packet size. */
                UInt16                                                          checksum; /* Check sum. */
                UInt32                                                          id;
                Byte                                                            cmd;
            };
            struct XndpRegisterEntryRequestPacket  : XndpPacket {
                Byte                                                            type; /* frp::configuration::MappingType */
                UInt16                                                          port;
            };                                     
            struct XndpReisterReplyPacket          : XndpPacket {
                Byte                                                            err;
            };                                     
            struct XndpQueryEntryReplyPacket       : XndpReisterReplyPacket {
                Byte                                                            type; /* frp::configuration::MappingType */
                UInt16                                                          port;
                Byte                                                            af;
                UInt16                                                          dst_port;
                Byte                                                            dst_addr[16];
            };                                     
            typedef XndpRegisterEntryRequestPacket XndpQueryEntryRequestPacket;
#pragma pack(pop)
            enum {
                XNDP_KF = 0x7E,
                XNDP_REGST_ENTRY = 0x2A, /* Registry entry. */
                XNDP_QUERY_ENTRY = 0x2E, /* Query entry. */
                XNDP_PUSH_MESSAGE = 0x2C,
                XNDP_ENTRY_TIMEOUT = 10000,
                XNDP_CALLER_TIMEOUT = 3000,
                XNDP_ERROR_SUCCESS = 0,
                XNDP_ERROR_INVALID_ENTRY_TYPE = 1,
                XNDP_ERROR_INVALID_ENTRY_PORT = 2,
                XNDP_ERROR_NOT_FOUND_ENTRY = 3,
            };
            typedef boost::asio::ip::udp::endpoint                              XNDP_ENDPOINT;
            struct XndpEntry {
                XNDP_ENDPOINT                                                   destinationEP; /* Mapped to a public address. */
                UInt64                                                          last;          /* Last activity times. */
                Byte                                                            type;
                UInt16                                                          port;
            };
            /* s mode. */
            typedef std::shared_ptr<XndpEntry>                                  XndpEntryPtr;
            typedef std::unordered_map<UInt16, XndpEntryPtr>                    XndpEntry2LevelTable;
            typedef std::unordered_map<Byte, XndpEntry2LevelTable>              XndpEntry1LevelTable;

        public:
            typedef std::function<void(
                UInt32                                                          id,
                Byte*                                                           buffer, 
                int                                                             length, 
                boost::asio::ip::udp::endpoint&                                 remoteEP)> MessageEventHandler;
            MessageEventHandler                                                 MessageEvent;

        public:
            Xndp(const std::shared_ptr<frp::threading::Hosting>& hosting) noexcept;
            Xndp(const std::shared_ptr<frp::threading::Hosting>& hosting, int bindport) noexcept;

        public:
            boost::asio::ip::udp::endpoint                                      GetLocalEndPoint() noexcept;
            boost::asio::ip::udp::socket&                                       GetSocket() noexcept;
            std::shared_ptr<boost::asio::io_context>                            GetContext() noexcept;
            std::shared_ptr<frp::threading::Hosting>                            GetHosting() noexcept;

        public:
            virtual bool                                                        IsAvailable() noexcept;
            virtual bool                                                        Open() noexcept;
            void                                                                Close() noexcept;
            virtual void                                                        Dispose() noexcept override;

        public:
            typedef std::function<void(bool)>                                   RegisterAsyncCallback;
            typedef std::function<void(bool, boost::asio::ip::udp::endpoint&)>  QueryAsyncCallback;
            virtual bool                                                        RegisterAsync(
                frp::configuration::MappingType                                 mapping_type, 
                int                                                             mapping_port,
                RegisterAsyncCallback                                           callback,
                const boost::asio::ip::udp::endpoint&                           destinationEP) noexcept;
            virtual bool                                                        QueryAsync(
                frp::configuration::MappingType                                 mapping_type,
                int                                                             mapping_port,
                QueryAsyncCallback                                              callback,
                const boost::asio::ip::udp::endpoint&                           destinationEP) noexcept;
            virtual bool                                                        PushAsync(
                UInt32                                                          id, 
                const void*                                                     message,
                const int                                                       message_size,
                const boost::asio::ip::udp::endpoint&                           destinationEP) noexcept;
            static boost::asio::ip::udp::endpoint                               ToEndPoint(const std::string host, int port) noexcept;

        protected:
            virtual void                                                        Timeout(UInt64 now) noexcept;

        private:
            bool                                                                SetTimeout() noexcept;
            int                                                                 PacketSerializeMini(XndpPacket* packet) noexcept;
            bool                                                                OnXndpProc() noexcept;
            bool                                                                OnRegisterEntryServer(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept;
            bool                                                                OnQueryEntryServer(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept;
            bool                                                                ReplyToDestination(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept;
            void                                                                ProcessEntryTimeout(UInt64 now) noexcept;

        private:
            /* c mode. */
            struct XndpCallerContext { /* rpc, stop wait ARQ. */
                std::shared_ptr<Byte>                                           message;
                int                                                             message_size;
                int                                                             retry_count;
                UInt64                                                          initial_time;
                XNDP_ENDPOINT                                                   destination;
                UInt32                                                          id;
                QueryAsyncCallback                                              query_ac;
                RegisterAsyncCallback                                           register_ac;
            };
            typedef std::unordered_map<UInt32, XndpCallerContext>               XndpCallerContextTable;
            bool                                                                OnRegisterEntryClient(XndpPacket* packet) noexcept;
            bool                                                                OnQueryEntryClient(XndpPacket* packet) noexcept;
            bool                                                                PopCallerContext(UInt32 id, XndpCallerContext& context) noexcept;
            void                                                                ProcessCallerTimeout(UInt64 now) noexcept;
            template<typename TCallback>
            bool                                                                RequireArgument(
                frp::configuration::MappingType                                 mapping_type,
                int                                                             mapping_port,
                TCallback&                                                      callback,
                const boost::asio::ip::udp::endpoint&                           destinationEP) noexcept;
            UInt32                                                              NewId() noexcept;
            template<typename TXndpRequestPacket>
            bool                                                                AddXndpCaller(
                Byte                                                            command,
                Byte                                                            mapping_type, 
                int                                                             mapping_port, 
                const QueryAsyncCallback&                                       qac, 
                const RegisterAsyncCallback&                                    rac,
                const boost::asio::ip::udp::endpoint&                           destinationEP) noexcept;
            void                                                                OnTimeoutError(XndpCallerContext& context) noexcept;

        private:
            std::atomic<bool>                                                   disposed_;
            std::atomic<UInt32>                                                 aid_;
            int                                                                 bindport_;
            std::shared_ptr<frp::threading::Hosting>                            hosting_;
            std::shared_ptr<boost::asio::io_context>                            context_;
            std::shared_ptr<Byte>                                               buffer_;
            boost::asio::ip::udp::socket                                        socket_;
            boost::asio::ip::udp::endpoint                                      localEP_;
            boost::asio::ip::udp::endpoint                                      endpoint_;
            boost::asio::deadline_timer                                         timeout_;
            XndpEntry1LevelTable                                                entries_;
            XndpCallerContextTable                                              callers_;
        };
    }
}

Xndp.cpp

#include <frp/discover/Xndp.h>
#include <frp/messages/checksum.h>
#include <frp/net/Ipep.h>
#include <frp/net/Socket.h>
#include <frp/collections/Dictionary.h>

using frp::configuration::MappingType;
using frp::net::AddressFamily;
using frp::net::Ipep;
using frp::net::IPEndPoint;
using frp::net::Socket;
using frp::collections::Dictionary;

namespace frp {
    namespace discover {
        Xndp::Xndp(const std::shared_ptr<frp::threading::Hosting>& hosting) noexcept
            : Xndp(hosting, IPEndPoint::MinPort) {

        }

        Xndp::Xndp(const std::shared_ptr<frp::threading::Hosting>& hosting, int bindport) noexcept
            : disposed_(false)
            , aid_(0)
            , bindport_(bindport)
            , hosting_(hosting)
            , context_(hosting->GetContext())
            , buffer_(hosting->GetBuffer())
            , socket_(*context_)
            , timeout_(*context_) {
            if (bindport < IPEndPoint::MinPort || bindport > IPEndPoint::MaxPort) {
                bindport = IPEndPoint::MinPort;
            }

            if (!Socket::OpenSocket(socket_, boost::asio::ip::address_v6::any(), bindport)) {
                bindport = IPEndPoint::MinPort;
            }
            else {
                bindport = Socket::LocalPort(socket_);
            }

            boost::system::error_code ec;
            if (bindport) {
                boost::asio::ip::udp::endpoint localEP = socket_.local_endpoint(ec);
                if (!ec) {
                    localEP_ = localEP;
                }
            }
        }

        boost::asio::ip::udp::endpoint Xndp::GetLocalEndPoint() noexcept {
            return localEP_;
        }

        std::shared_ptr<boost::asio::io_context> Xndp::GetContext() noexcept {
            return context_;
        }

        std::shared_ptr<frp::threading::Hosting> Xndp::GetHosting() noexcept {
            return hosting_;
        }

        bool Xndp::IsAvailable() noexcept {
            return !disposed_ && socket_.is_open();
        }

        void Xndp::Close() noexcept {
            Dispose();
        }

        void Xndp::Dispose() noexcept {
            if (!disposed_.exchange(true)) {
                /* Releases the socket and timer held. */
                Socket::Closesocket(socket_);
                frp::threading::Hosting::Cancel(timeout_);

                /* Releases all managed table member data. */
                entries_.clear();
                Dictionary::ReleaseAllPairs(callers_,
                    [this](XndpCallerContext& context) noexcept {
                        OnTimeoutError(context);
                    });

                /* Deletes the message event listener bound to the current class instance object. */
                MessageEvent = MessageEventHandler();
            }
        }

        bool Xndp::Open() noexcept {
            if (!IsAvailable()) {
                return false;
            }

            return SetTimeout() && OnXndpProc();
        }

        bool Xndp::SetTimeout() noexcept {
            if (disposed_) {
                return false;
            }

            std::shared_ptr<Reference> reference = GetReference();
            timeout_.expires_from_now(boost::posix_time::milliseconds(1000));
            timeout_.async_wait(
                [reference, this](const boost::system::error_code& ec) noexcept {
                    if (!ec) {
                        Timeout(hosting_->CurrentMillisec());
                        SetTimeout();
                    }
                });
            return true;
        }

        void Xndp::Timeout(UInt64 now) noexcept {
            ProcessEntryTimeout(now);
            ProcessCallerTimeout(now);
        }

        void Xndp::ProcessEntryTimeout(UInt64 now) noexcept {
            XndpEntry1LevelTable::iterator tail = entries_.begin();
            XndpEntry1LevelTable::iterator endl = entries_.end();
            if (tail != endl) {
                std::vector<XndpEntryPtr> releases;
                for (; tail != endl; tail++) {
                    XndpEntry2LevelTable::iterator tail2 = tail->second.begin();
                    XndpEntry2LevelTable::iterator endl2 = tail->second.end();
                    for (; tail2 != endl2; tail2++) {
                        XndpEntryPtr& entry = tail2->second;
                        if (entry->last > now) {
                            releases.push_back(std::move(entry));
                        }
                        else {
                            UInt64 diff = now - entry->last;
                            if (diff >= XNDP_ENTRY_TIMEOUT) {
                                releases.push_back(std::move(entry));
                            }
                        }
                    }
                }

                for (std::size_t index = 0, length = releases.size(); index < length; index++) {
                    XndpEntryPtr& entry = releases[index];
                    if (entry) {
                        Dictionary::TryRemove2Layer(entries_, entry->type, entry->port);
                    }
                }
            }
        }

        void Xndp::ProcessCallerTimeout(UInt64 now) noexcept {
            XndpCallerContextTable::iterator tail = callers_.begin();
            XndpCallerContextTable::iterator endl = callers_.end();
            if (tail != endl) {
                std::vector<UInt32> releases;
                for (; tail != endl; tail++) {
                    XndpCallerContext& context = tail->second;
                    if (!IsAvailable()) {
                        releases.push_back(context.id);
                        continue;
                    }

                    UInt64 initial_time = context.initial_time;
                    if (initial_time > now) {
                        releases.push_back(context.id);
                        continue;
                    }

                    UInt64 diff = now - initial_time;
                    if (diff >= XNDP_CALLER_TIMEOUT) {
                        releases.push_back(context.id);
                        continue;
                    }

                    UInt32 retry_count = ++context.retry_count;
                    if (retry_count >= 3) {
                        releases.push_back(context.id);
                        continue;
                    }

                    boost::system::error_code ec;
                    socket_.send_to(boost::asio::buffer(context.message.get(), context.message_size), context.destination, 0, ec);
                }

                for (std::size_t index = 0, length = releases.size(); index < length; index++) {
                    XndpCallerContext context;
                    if (Dictionary::TryRemove(callers_, releases[index], context)) {
                        OnTimeoutError(context);
                    }
                }
            }
        }

        void Xndp::OnTimeoutError(XndpCallerContext& context) noexcept {
            if (context.register_ac) {
                context.register_ac(false);
            }

            if (context.query_ac) {
                boost::asio::ip::udp::endpoint nanoEP;
                context.query_ac(false, nanoEP);
            }
        }

        bool Xndp::OnXndpProc() noexcept {
            if (!IsAvailable()) {
                return false;
            }

            std::shared_ptr<Reference> reference = GetReference();
            socket_.async_receive_from(boost::asio::buffer(buffer_.get(), frp::threading::Hosting::BufferSize), endpoint_,
                [reference, this](const boost::system::error_code& ec, std::size_t sz) noexcept {
                    /* If it is an Xndp packet, the packet is processed; otherwise, the packet is displayed. */
                    int length = std::max<int>(ec ? -1 : sz, -1);
                    if (length >= sizeof(XndpPacket)) {
                        XndpPacket* packet = (XndpPacket*)buffer_.get();
                        do {
                            /* The key frame is inconsistent. */
                            if (packet->kf != XNDP_KF) {
                                break;
                            }

                            /* Check frame length validity. */
                            int len = ntohs(packet->len);
                            if (len != length) {
                                break;
                            }

                            /* Check packet checksum. */
                            int checksum = frp::messages::inet_chksum(packet, len);
                            if (checksum != 0) {
                                break;
                            }
    
                            /* This is a malicious external attack. */
                            if (packet->cmd != XNDP_REGST_ENTRY && packet->cmd != XNDP_QUERY_ENTRY && packet->cmd != XNDP_PUSH_MESSAGE) {
                                break;
                            }

                            /* Converts data from network byte order to local byte order. */
                            packet->len = len;
                            packet->id = ntohl(packet->id);

                            /* The action of processing the request. */
                            switch (packet->cmd) {
                            case XNDP_REGST_ENTRY:
                                if (bindport_) {
                                    OnRegisterEntryServer(packet, endpoint_);
                                }
                                else {
                                    OnRegisterEntryClient(packet);
                                }
                                break;
                            case XNDP_QUERY_ENTRY:
                                if (bindport_) {
                                    OnQueryEntryServer(packet, endpoint_);
                                }
                                else {
                                    OnQueryEntryClient(packet);
                                }
                                break;
                            case XNDP_PUSH_MESSAGE:
                                /* If the packets are not Xndp packets, they belong to other protocols that pass through the Intranet. */
                                int message_size = len - sizeof(XndpPacket);
                                if (message_size > 0) {
                                    MessageEventHandler handler = MessageEvent;
                                    if (handler) {
                                        handler(packet->id, (Byte*)(packet + 1), message_size, endpoint_);
                                    }
                                }
                                break;
                            };
                        } while (0);
                    }
                    OnXndpProc();
                });
            return true;
        }

        bool Xndp::OnRegisterEntryServer(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept {
            XndpRegisterEntryRequestPacket* request = (XndpRegisterEntryRequestPacket*)packet;
            if (request->len < sizeof(XndpRegisterEntryRequestPacket)) {
                return false;
            }
            else {
                request->port = ntohs(request->port);
            }

            XndpReisterReplyPacket reply;
            reply.checksum = 0;
            reply.id = packet->id;
            reply.cmd = packet->cmd;
            reply.len = sizeof(reply);
            reply.err = XNDP_ERROR_SUCCESS;

            if (request->type < MappingType::MappingType_TCP || request->type >= MappingType::MappingType_MaxType) {
                reply.err = XNDP_ERROR_INVALID_ENTRY_TYPE;
            }
            elif(request->port <= IPEndPoint::MinPort || request->port > IPEndPoint::MaxPort) {
                reply.err = XNDP_ERROR_INVALID_ENTRY_PORT;
            }
            else {
                XndpEntryPtr& entry = entries_[request->type][request->port];
                if (!entry) {
                    entry = make_shared_object<XndpEntry>();
                }

                entry->destinationEP = IPEndPoint::ToEndPoint<boost::asio::ip::udp>(IPEndPoint::V6ToV4(IPEndPoint::ToEndPoint(remoteEP)));
                entry->port = request->port;
                entry->type = request->type;
                entry->last = hosting_->CurrentMillisec();
            }
            return ReplyToDestination(&reply, remoteEP);
        }

        bool Xndp::OnQueryEntryServer(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept {
            XndpQueryEntryRequestPacket* request = (XndpQueryEntryRequestPacket*)packet;
            if (request->len < sizeof(XndpQueryEntryRequestPacket)) {
                return false;
            }
            else {
                request->port = ntohs(request->port);
            }

            XndpQueryEntryReplyPacket reply;
            reply.checksum = 0;
            reply.id = packet->id;
            reply.cmd = packet->cmd;
            reply.len = sizeof(reply);
            reply.err = XNDP_ERROR_SUCCESS;
            reply.type = IPEndPoint::MinPort;
            reply.port = MappingType::MappingType_TCP;

            if (request->type < MappingType::MappingType_TCP || request->type >= MappingType::MappingType_MaxType) {
                reply.err = XNDP_ERROR_INVALID_ENTRY_TYPE;
            }
            elif(request->port <= IPEndPoint::MinPort || request->port > IPEndPoint::MaxPort) {
                reply.err = XNDP_ERROR_INVALID_ENTRY_PORT;
            }
            else {
                XndpEntryPtr entry;
                if (!Dictionary::TryGetValue2Layer(entries_, request->type, request->port, entry) || !entry) {
                    reply.err = XNDP_ERROR_NOT_FOUND_ENTRY;
                }
                else {
                    boost::asio::ip::address address = entry->destinationEP.address();
                    if (address.is_v4()) {
                        boost::asio::ip::address_v4::bytes_type address_bytes = address.to_v4().to_bytes();
                        memcpy(reply.dst_addr, address_bytes.data(), address_bytes.size());
                        reply.af = AddressFamily::InterNetwork;
                    }
                    else {
                        boost::asio::ip::address_v6::bytes_type address_bytes = address.to_v6().to_bytes();
                        memcpy(reply.dst_addr, address_bytes.data(), address_bytes.size());
                        reply.af = AddressFamily::InterNetworkV6;
                    }
                    reply.type = entry->type;
                    reply.port = htons(entry->port);
                    reply.dst_port = htons(entry->destinationEP.port());
                }
            }
            return ReplyToDestination(&reply, remoteEP);
        }

        template<typename TCallback>
        bool Xndp::RequireArgument(
            frp::configuration::MappingType       mapping_type,
            int                                   mapping_port,
            TCallback&                            callback,
            const boost::asio::ip::udp::endpoint& destinationEP) noexcept {
            typedef frp::net::IPEndPoint IPEndPoint;
            typedef frp::configuration::MappingType MappingType;

            if (!IsAvailable()) {
                return false;
            }
            elif(!callback) {
                return false;
            }
            elif(mapping_type < MappingType::MappingType_TCP || mapping_type >= MappingType::MappingType_MaxType) {
                return false;
            }
            elif(mapping_port <= IPEndPoint::MinPort || mapping_port > IPEndPoint::MaxPort) {
                return false;
            }

            boost::asio::ip::address address = destinationEP.address();
            if (address.is_multicast() || address.is_unspecified()) {
                return false;
            }
            return true;
        }

        template<typename TXndpRequestPacket>
        bool Xndp::AddXndpCaller(
            Byte                                  command,
            Byte                                  mapping_type,
            int                                   mapping_port,
            const QueryAsyncCallback&             qac,
            const RegisterAsyncCallback&          rac,
            const boost::asio::ip::udp::endpoint& destinationEP) noexcept {
            typedef frp::net::IPEndPoint IPEndPoint;

            int message_size = sizeof(TXndpRequestPacket);
            std::shared_ptr<Byte> message = make_shared_alloc<Byte>(message_size);
            if (!message) {
                return false;
            }

            UInt32 id = NewId();
            TXndpRequestPacket* request = (TXndpRequestPacket*)message.get();
            request->checksum = 0;
            request->cmd = command;
            request->id = id;
            request->len = message_size;
            request->type = mapping_type;
            request->port = htons(mapping_port);
            if (PacketSerializeMini(request) < 1) {
                return false;
            }

            XNDP_ENDPOINT remoteEP = IPEndPoint::ToEndPoint<boost::asio::ip::udp>(IPEndPoint::V4ToV6(IPEndPoint::ToEndPoint(destinationEP)));
            XndpCallerContext& context = callers_[id];
            context.id = id;
            context.destination = remoteEP;
            context.initial_time = hosting_->CurrentMillisec();
            context.query_ac = qac;
            context.register_ac = rac;
            context.retry_count = 0;
            context.message = message;
            context.message_size = message_size;

            boost::system::error_code ec;
            socket_.send_to(boost::asio::buffer(request, message_size), remoteEP, 0, ec);
            return ec ? false : true;
        }

        int Xndp::PacketSerializeMini(XndpPacket* packet) noexcept {
            int length = packet->len;
            if (length < sizeof(XndpPacket)) {
                return 0;
            }

            packet->kf = XNDP_KF;
            packet->checksum = 0;
            packet->id = htonl(packet->id);
            packet->len = htons(length);

            UInt16 checksum = frp::messages::inet_chksum(packet, length);
            if (checksum == 0) {
                checksum = 0xffff;
            }
            packet->checksum = htons(checksum);
            return length;
        }

        bool Xndp::ReplyToDestination(XndpPacket* packet, const boost::asio::ip::udp::endpoint& remoteEP) noexcept {
            if (!IsAvailable()) {
                return false;
            }

            int length = PacketSerializeMini(packet);
            if (length < 1) {
                return false;
            }

            boost::system::error_code ec;
            socket_.send_to(boost::asio::buffer(packet, length), remoteEP, 0, ec);
            return ec ? false : true;
        }

        bool Xndp::RegisterAsync(MappingType mapping_type, int mapping_port, RegisterAsyncCallback callback, const boost::asio::ip::udp::endpoint& destinationEP) noexcept {
            if (!RequireArgument(mapping_type, mapping_port, callback, destinationEP)) {
                return false;
            }

            return AddXndpCaller<XndpRegisterEntryRequestPacket>(XNDP_REGST_ENTRY,
                mapping_type, mapping_port, NULL, callback, destinationEP);
        }

        bool Xndp::QueryAsync(MappingType mapping_type, int mapping_port, QueryAsyncCallback callback, const boost::asio::ip::udp::endpoint& destinationEP) noexcept {
            if (!RequireArgument(mapping_type, mapping_port, callback, destinationEP)) {
                return false;
            }

            return AddXndpCaller<XndpQueryEntryRequestPacket>(XNDP_QUERY_ENTRY,
                mapping_type, mapping_port, callback, NULL, destinationEP);
        }

        bool Xndp::PushAsync(UInt32 id, const void* message, const int message_size, const boost::asio::ip::udp::endpoint& destinationEP) noexcept {
            if (!message || message_size < 1) {
                return false;
            }

            int chunk_size = sizeof(XndpPacket) + message_size;
            std::shared_ptr<Byte> chunk = make_shared_alloc<Byte>(chunk_size);
            if (!message) {
                return false;
            }

            XndpPacket* packet = (XndpPacket*)chunk.get();
            packet->checksum = 0;
            packet->cmd = XNDP_PUSH_MESSAGE;
            packet->id = id;
            packet->len = chunk_size;
            memcpy(packet + 1, message, message_size);

            if (PacketSerializeMini(packet) < 1) {
                return false;
            }

            boost::system::error_code ec;
            socket_.send_to(boost::asio::buffer(packet, chunk_size), destinationEP, 0, ec);
            return ec ? false : true;
        }

        UInt32 Xndp::NewId() noexcept {
            for (;;) {
                int id = aid_++;
                if (!id) {
                    continue;
                }

                if (!Dictionary::ContainsKey(callers_, id)) {
                    return id;
                }
            }
        }

        boost::asio::ip::udp::socket& Xndp::GetSocket() noexcept {
            return socket_;
        }

        bool Xndp::PopCallerContext(UInt32 id, XndpCallerContext& context) noexcept {
            return Dictionary::TryRemove(callers_, id, context);
        }

        bool Xndp::OnRegisterEntryClient(XndpPacket* packet) noexcept {
            XndpReisterReplyPacket* reply = (XndpReisterReplyPacket*)packet;
            if (reply->len < sizeof(XndpReisterReplyPacket)) {
                return false;
            }

            XndpCallerContext caller;
            if (!PopCallerContext(reply->id, caller)) {
                return false;
            }

            caller.register_ac(reply->err == XNDP_ERROR_SUCCESS);
            return true;
        }

        bool Xndp::OnQueryEntryClient(XndpPacket* packet) noexcept {
            XndpQueryEntryReplyPacket* reply = (XndpQueryEntryReplyPacket*)packet;
            if (reply->len < sizeof(XndpQueryEntryReplyPacket)) {
                return false;
            }

            XndpCallerContext caller;
            if (!PopCallerContext(reply->id, caller)) {
                return false;
            }

            if (reply->err != XNDP_ERROR_SUCCESS) {
                boost::asio::ip::udp::endpoint noneEP;
                caller.query_ac(false, noneEP);
            }
            else {
                boost::asio::ip::address address;
                if (reply->af == AddressFamily::InterNetwork) {
                    address = boost::asio::ip::address_v4(*(boost::asio::ip::address_v4::bytes_type*)reply->dst_addr);
                }
                else {
                    address = boost::asio::ip::address_v6(*(boost::asio::ip::address_v6::bytes_type*)reply->dst_addr);
                }

                boost::asio::ip::udp::endpoint destinationEP(address, ntohs(reply->dst_port));
                caller.query_ac(true, destinationEP);
            }
            return true;
        }

        boost::asio::ip::udp::endpoint Xndp::ToEndPoint(const std::string host, int port) noexcept {
            return IPEndPoint::ToEndPoint<boost::asio::ip::udp>(Ipep::GetEndPoint(host, port));
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值