#include "HDSDK.h" #include #include #include #include #include #include #define MD5_LENGHT (32) #define XML_MAX (9200) #define LOCAL_TCP_VERSION (0x1000009) #define LOCAL_UDP_VERSION (0x1000009) typedef unsigned long long huint64; typedef unsigned int huint32; typedef unsigned short huint16; typedef unsigned char huint8; typedef long long hint64; typedef int hint32; typedef short hint16; typedef char hint8; typedef float hfloat; typedef double hdouble; namespace detail { enum eHCmdType { kTcpHeartbeatAsk = 0x005f, ///< TCP心跳包请求 kTcpHeartbeatAnswer = 0x0060, ///< TCP心跳包反馈 kSearchDeviceAsk = 0x1001, ///< 搜索设备请求 kSearchDeviceAnswer = 0x1002, ///< 搜索设备应答 kErrorAnswer = 0x2000, ///< 出错反馈 kSDKServiceAsk = 0x2001, ///< 版本协商请求 kSDKServiceAnswer = 0x2002, ///< 版本协商应答 kSDKCmdAsk = 0x2003, ///< sdk命令请求 kSDKCmdAnswer = 0x2004, ///< sdk命令反馈 kFileStartAsk = 0x8001, ///< 文件开始传输请求 kFileStartAnswer = 0x8002, ///< 文件开始传输应答 kFileContentAsk = 0x8003, ///< 携带文件内容的请求 kFileContentAnswer = 0x8004, ///< 写文件内容的应答 kFileEndAsk = 0x8005, ///< 文件结束传输请求 kFileEndAnswer = 0x8006, ///< 文件结束传输应答 kReadFileAsk = 0x8007, ///< 回读文件请求 kReadFileAnswer = 0x8008, ///< 回读文件应答 }; template class HCallBack { public: HCallBack() : userData_(nullptr) {} void Bind(const std::function<_Func> &func) { callBack_ = func; } void ClearFunc() { callBack_ = std::function<_Func>();} void SetUserData(void *userData) { userData_ = userData; } template #if __cplusplus >= 201402L decltype(auto) #else typename std::result_of(_Args..., void *)>::type #endif Emit(_Args &&...args) { auto func(callBack_); return func(std::forward<_Args>(args)..., userData_); } template #if __cplusplus >= 201402L decltype(auto) #else typename std::result_of(_Args..., void *)>::type #endif operator()(_Args &&...args) { return this->Emit(std::forward<_Args>(args)...); } bool IsCanUse() const { return bool(callBack_); } private: std::function<_Func> callBack_; void *userData_; }; class HAny { private: struct DataBase; using DataBasePtrType = std::shared_ptr; struct DataBase{ virtual ~DataBase() {} virtual DataBasePtrType Clone() const = 0; virtual const std::type_info &Type() const = 0; }; template struct Data : public DataBase { template Data(Args&&...args) : data(std::forward(args)...){ } virtual DataBasePtrType Clone() const override { return DataBasePtrType(new Data(data)); } virtual const std::type_info &Type() const override { return typeid(_T); } _T data; }; DataBasePtrType Clone() const { if (data_) { return data_->Clone(); } return nullptr; } public: HAny() {} HAny(const HAny &other) : data_(other.data_) {} template ::type, HAny>::value, _T>::type> HAny(_T &&t) : data_(new Data::type>(std::forward<_T>(t))) {} const std::type_info &Type() const { return data_ ? data_->Type() : typeid(void); } template bool IsType() const { return Type() == typeid(_T); } template _T& Cast() const { Detach(); auto p = static_cast::type>*>(this->data_.get()); return p->data; } private: void Detach() const { if (!data_ || data_.unique()) { return ; } data_ = data_->Clone(); } private: mutable DataBasePtrType data_; }; template std::string IconvStr(_T value) { std::string ret; for (std::size_t i = 0; i < sizeof(_T); ++i) { ret.push_back(char(value & 0xff)); value = value >> 8; } return ret; } template _T StrIconv(const char *value, std::size_t len = std::numeric_limits::max()) { _T ret = _T(); for (std::size_t i = 0; i < sizeof(typename std::remove_cv<_T>::type) && i < len; ++i) { ret = ret | ((value[i] & 0xff) << (8 * i)); } return ret; } template _T StrIconv(const std::string &value) { return StrIconv<_T>(value.data(), value.size()); } static std::list SplitSdkData(const std::string &xml) { std::list queue; int size = static_cast(xml.size()); huint32 index = 0; for (; size > XML_MAX; size -= XML_MAX, index += XML_MAX){ std::string data = IconvStr(12 + XML_MAX); data.append(IconvStr(kSDKCmdAsk)); data.append(IconvStr(xml.size())); data.append(IconvStr(index)); data.append(xml.data() + index, XML_MAX); queue.emplace_back(std::move(data)); } if (size > 0){ std::string data = IconvStr(12 + static_cast(size)); data.append(IconvStr(kSDKCmdAsk)); data.append(IconvStr(xml.size())); data.append(IconvStr(index)); data.append(xml.data() + index, size); queue.emplace_back(std::move(data)); } return queue; } static std::list SplitFileData(const char *fileData, int size) { std::list queue; huint32 index = 0; for (; size > XML_MAX; size -= XML_MAX, index += XML_MAX){ std::string data = IconvStr(4 + XML_MAX); data.append(IconvStr(kFileContentAsk)); data.append(fileData + index, XML_MAX); queue.emplace_back(std::move(data)); } if (size > 0){ std::string data = IconvStr(4 + static_cast(size)); data.append(IconvStr(kFileContentAsk)); data.append(fileData + index, size); queue.emplace_back(std::move(data)); } return queue; } static std::string &Remove(std::string &buff, std::size_t index, std::size_t num = std::string::npos) { if (index > buff.size()) { index = buff.size(); } return buff.erase(index, num); } static std::string Mid(const std::string &buff, std::size_t index, std::size_t len = std::string::npos) { if (index >= buff.size()) { return std::string(); } if (len > buff.size()) { len = buff.size(); } if (len > buff.size() - index) { len = buff.size() - index; } if (index == 0 && len == buff.size()) { return buff; } return buff.substr(index, len); } static std::string GetXmlAttribute(const std::string &xml, const std::string &node, const std::string &attributeName) { std::string nodeName = "<" + node; std::string::size_type pos = xml.find(nodeName); if (pos == std::string::npos) { return ""; } pos = xml.find(attributeName, pos + nodeName.size()); if (pos == std::string::npos) { return ""; } pos = xml.find('"', pos + attributeName.size()); if (pos == std::string::npos) { return ""; } ++pos; std::string::size_type posEnd = xml.find('"', pos); if (posEnd == std::string::npos) { return ""; } return xml.substr(pos, posEnd - pos); } static std::string SetXmlAttribute(const std::string &xml, const std::string &node, const std::string &attributeName, const std::string &attributeValue) { std::string nodeName = "<" + node; std::string::size_type pos = xml.find(nodeName); if (pos == std::string::npos) { return xml; } pos = xml.find(attributeName, pos + nodeName.size()); if (pos == std::string::npos) { return xml; } pos = xml.find('"', pos + attributeName.size()); if (pos == std::string::npos) { return xml; } ++pos; std::string::size_type posEnd = xml.find('"', pos); if (posEnd == std::string::npos) { return xml; } std::string result = xml; return result.replace(pos, posEnd - pos, attributeValue); } struct DelaySend { int cmd; std::string data; detail::HAny userData; DelaySend() : cmd(0) {} DelaySend(int c, const std::string &d, const detail::HAny &u) : cmd(c) , data(d) , userData(u) {} DelaySend(int c, std::string &&d, detail::HAny &&u) : cmd(c) , data(std::move(d)) , userData(std::move(u)) {} }; struct SendFileInfo { hint64 index; hint64 size; hint16 type; std::string md5; std::string name; std::function call; void *userData; SendFileInfo() : index(0) , size(0) , type(0) , userData(nullptr) {} void Clear() { index = 0; size = 0; type = 0; md5.clear(); name.clear(); call = std::function(); userData = nullptr; } }; ///< 绑定成员函数的回调 template std::function<_Ret(_Args...)> BindMember(_Obj *obj, _Ret(_Obj::*func)(_Args...)) { return [&](_Args &&...args) { return (obj->*func)(std::forward<_Args>(args)...); }; } ///< 绑定普通函数的回调 template std::function<_Ret(_Args...)> BindFunc(_Ret(*func)(_Args...)) { return [&](_Args &&...args) { return func(std::forward<_Args>(args)...); }; } } namespace hd { ///< SDK私有数据类, 用于保留接口一致 class HDSDKPrivate { public: detail::HCallBack NotifyReadXml; detail::HCallBack NotifySendData; detail::HCallBack NotifySendFile; public: enum eCmd { kInitRun, ///< 初始化协商 kReadData, ///< 读取数据 kSendSDK, ///< 发送xml kSendFile, ///< 发送文件 }; public: HDSDKPrivate() { Init(); } void Init(); bool Dispose(int cmd, const std::string &data, const detail::HAny &userData = detail::HAny()); private: bool ParseReadData(const std::string &data); bool ParseNegotiate(int num); bool ParseSDKCmdAnswer(std::string &&data); bool ParseSendSDK(int cmd, const std::string &data, const detail::HAny &userData); bool ParseFileStartAnswer(std::string &&buff); bool SendFileEnd(); bool DelaySend(); ///< 通知回调接口处理任务 bool SendData(const std::string &data) { return SendData(data.c_str(), data.size()); } bool SendData(const char *data, int len); void ReadXml(const std::string &xml, int errorCode); int SendFile(hint64 index, hint64 size, int status, char *buff, int buffSize); private: friend class HDSDK; std::string guid_; ///< 通信的guid std::string sdkBuff_; ///< sdk xml缓存 std::string readBuff_; ///< 总数据缓存 std::list delaySendList_; ///< 延迟发送队列 detail::SendFileInfo fileInfo_; ///< 发送文件信息 bool negotiate_; ///< 协商状态 bool processing_; ///< 发送文件中独占 }; void HDSDKPrivate::Init() { guid_.clear(); negotiate_ = false; processing_ = false; delaySendList_.clear(); std::string().swap(sdkBuff_); std::string().swap(readBuff_); } bool HDSDKPrivate::Dispose(int cmd, const std::string &data, const detail::HAny &userData) { switch (cmd) { case kInitRun: { return ParseNegotiate(0); } break; case kReadData: { return ParseReadData(data); } break; default: break; } if (processing_) { delaySendList_.emplace_back(detail::DelaySend(cmd, data, userData)); return true; } switch (cmd) { case kSendSDK: { return ParseSendSDK(cmd, data, userData); } break; case kSendFile: { if (negotiate_ == false) { delaySendList_.emplace_back(detail::DelaySend(cmd, data, userData)); return true; } if (userData.IsType() == false) { return false; } fileInfo_ = userData.Cast(); if (fileInfo_.size <= 0 || fileInfo_.md5.empty() || fileInfo_.name.empty()) { return false; } if (fileInfo_.md5.size() < MD5_LENGHT) { fileInfo_.md5.append(MD5_LENGHT - fileInfo_.md5.size(), '\0'); } NotifySendFile.Bind(fileInfo_.call); NotifySendFile.SetUserData(fileInfo_.userData); std::string sendData = detail::IconvStr(47 + fileInfo_.name.size() + 1); sendData.append(detail::IconvStr(detail::kFileStartAsk)); sendData.append(fileInfo_.md5); sendData.append(1, '\0'); sendData.append(detail::IconvStr(fileInfo_.size)); sendData.append(detail::IconvStr(fileInfo_.type)); sendData.append(fileInfo_.name); sendData.append(1, '\0'); if (SendData(sendData) == false) { return false; } processing_ = true; return true; } break; default: break; } return false; } bool HDSDKPrivate::ParseReadData(const std::string &data) { if (data.empty() == false) { readBuff_.append(data); } if (readBuff_.size() < 4) { return true; } std::size_t len = detail::StrIconv(readBuff_.c_str(), readBuff_.size()); int cmd = detail::StrIconv(readBuff_.c_str() + 2, readBuff_.size() - 2); if (len > readBuff_.size()) { return true; } if (len == 0) { return false; } std::string buff = detail::Mid(data, 0, len); detail::Remove(readBuff_, 0, len); bool result = true; switch (cmd) { case detail::kTcpHeartbeatAsk: case detail::kTcpHeartbeatAnswer: { std::string sendData = detail::IconvStr(4); sendData.append(detail::IconvStr(detail::kTcpHeartbeatAsk)); result = SendData(sendData); } break; case detail::kSDKServiceAnswer: { result = ParseNegotiate(1); } break; case detail::kSDKCmdAnswer: { result = ParseSDKCmdAnswer(std::move(buff)); } break; case detail::kErrorAnswer: { if (buff.size() < 6) { return false; } ReadXml("", detail::StrIconv(buff.c_str() + 4, buff.size() - 4)); } break; case detail::kFileStartAnswer: { result = ParseFileStartAnswer(std::move(buff)); } break; case detail::kFileContentAnswer: break; case detail::kFileEndAnswer: { SendFile(fileInfo_.index, fileInfo_.size, 0, nullptr, 0); processing_ = false; result = DelaySend(); } break; default: break; } if (result && readBuff_.empty() == false) { return ParseReadData(""); } return result; } bool HDSDKPrivate::ParseNegotiate(int num) { std::string data; if (num == 0) { data.append(detail::IconvStr(8)); data.append(detail::IconvStr(detail::kSDKServiceAsk)); data.append(detail::IconvStr(LOCAL_TCP_VERSION)); } else { std::string xml(R"( )"); data = detail::SplitSdkData(xml).front(); } return SendData(data); } bool HDSDKPrivate::ParseSDKCmdAnswer(std::string &&data) { if (data.size() < 12) { return false; } // int len = detail::StrIconv(data.c_str()); // int cmd = detail::StrIconv(data.c_str() + 2, data.size() - 2); huint32 total = detail::StrIconv(data.c_str() + 4, data.size() - 4); // int index = detail::StrIconv(data.c_str() + 8, data.size() - 8); detail::Remove(data, 0, 12); if (negotiate_ == false) { guid_ = detail::GetXmlAttribute(data, "sdk", "guid"); negotiate_ = true; return DelaySend(); } if (data.empty()) { return true; } sdkBuff_.append(data); if (total <= sdkBuff_.size()) { std::string buff(std::move(sdkBuff_)); sdkBuff_.clear(); ReadXml(buff, 0); } return true; } bool HDSDKPrivate::ParseSendSDK(int cmd, const std::string &data, const detail::HAny &userData) { if (negotiate_ == false) { delaySendList_.emplace_back(detail::DelaySend(cmd, data, userData)); return true; } if (data.empty()) { return true; } std::string xml = detail::SetXmlAttribute(data, "sdk", "guid", guid_); auto xmlList = detail::SplitSdkData(xml); for (const auto &i : xmlList) { if (SendData(i) == false) { return false; } } return true; } bool HDSDKPrivate::ParseFileStartAnswer(std::string &&buff) { if (buff.size() < 14) { return false; } processing_ = true; huint16 status = detail::StrIconv(buff.c_str() + 4, buff.size() - 4); fileInfo_.index = detail::StrIconv(buff.c_str() + 6, buff.size() - 6); if (status != 0) { SendFile(fileInfo_.index, fileInfo_.size, status, nullptr, 0); processing_ = false; return DelaySend(); } std::vector sendBuff(XML_MAX, '\0'); while (fileInfo_.index < fileInfo_.size) { int len = SendFile(fileInfo_.index, fileInfo_.size, status, sendBuff.data(), sendBuff.size()); if (len <= 0) { processing_ = false; return false; } fileInfo_.index += len; auto sendList = detail::SplitFileData(sendBuff.data(), len); for (const auto &i : sendList) { if (SendData(i) == false) { processing_ = false; return false; } } } return SendFileEnd(); } bool HDSDKPrivate::SendFileEnd() { std::string data = detail::IconvStr(4); data.append(detail::IconvStr(detail::kFileEndAsk)); return SendData(data); } bool HDSDKPrivate::DelaySend() { std::list sendList; sendList.swap(delaySendList_); while (sendList.empty() == false) { if (processing_) { delaySendList_.splice(delaySendList_.begin(), std::move(sendList)); break; } detail::DelaySend i = std::move(sendList.front()); sendList.pop_front(); if (Dispose(i.cmd, i.data, i.userData) == false) { delaySendList_.clear(); return false; } } return true; } bool HDSDKPrivate::SendData(const char *data, int len) { if (NotifySendData.IsCanUse() == false) { return false; } return NotifySendData(data, len); } void HDSDKPrivate::ReadXml(const std::string &xml, int errorCode) { if (NotifyReadXml.IsCanUse() == false) { return ; } NotifyReadXml(xml.c_str(), xml.size(), errorCode); } int HDSDKPrivate::SendFile(hint64 index, hint64 size, int status, char *buff, int buffSize) { if (NotifySendFile.IsCanUse() == false) { return -1; } return NotifySendFile(index, size, status, buff, buffSize); } } IHDProtocol CreateProtocol() { return new hd::HDSDKPrivate(); } void FreeProtocol(IHDProtocol protocol) { delete protocol; } HBool SetProtocolFunc(IHDProtocol protocol, int func, void *data) { if (protocol == nullptr) { return HFalse; } switch (func) { case kSetReadXml: { if (data) { protocol->NotifyReadXml.Bind(reinterpret_cast(data)); } else { protocol->NotifyReadXml.ClearFunc(); } } break; case kSetReadXmlData: { protocol->NotifyReadXml.SetUserData(data); } break; case kSetSendFunc: { if (data) { protocol->NotifySendData.Bind(reinterpret_cast(data)); } else { protocol->NotifySendData.ClearFunc(); } } break; case kSetSendFuncData: { protocol->NotifySendData.SetUserData(data); } break; default: return HFalse; break; } return HTrue; } void InitProtocol(hd::IHDProtocol protocol) { protocol->Init(); } HBool RunProtocol(hd::IHDProtocol protocol) { return protocol->Dispose(hd::HDSDKPrivate::kInitRun, "") ? HTrue : HFalse; } HBool UpdateReadData(hd::IHDProtocol protocol, const char *data, int len) { return protocol->Dispose(hd::HDSDKPrivate::kReadData, std::string(data, len)) ? HTrue : HFalse; } HBool SendXml(hd::IHDProtocol protocol, const char *xml, int len) { return protocol->Dispose(hd::HDSDKPrivate::kSendSDK, std::string(xml, len)) ? HTrue : HFalse; } HBool SendFile(hd::IHDProtocol protocol, const char *fileName, int fileNameLen, const char *md5, int type, hint64 size, int (*callFun)(hint64, hint64, int, char *, int, void *), void *userData) { detail::SendFileInfo info; info.size = size; info.type = type; info.md5.assign(md5); info.name.assign(fileName, fileNameLen); info.call = callFun; info.userData = userData; if (info.md5.empty() || info.md5.size() != MD5_LENGHT) { return HFalse; } if (info.name.empty()) { return HFalse; } return protocol->Dispose(hd::HDSDKPrivate::kSendFile, "", info) ? HTrue : HFalse; }