diff --git a/Link/LinkClient.cpp b/Link/LinkClient.cpp index cfc296ff..671fef89 100644 --- a/Link/LinkClient.cpp +++ b/Link/LinkClient.cpp @@ -26,12 +26,7 @@ LinkClient::LinkClient() Delay = 0; MaxNotActive = 0; - NextReport = -1; - ReportLength = 0; - - _Expect = nullptr; - - assert(!Current, "只能有一个令牌客户端实例"); + assert(!Current, "只能有一个物联客户端实例"); Current = this; } @@ -42,7 +37,7 @@ void LinkClient::Open() TS("LinkClient::Open"); // 令牌客户端定时任务 - _task = Sys.AddTask(&LinkClient::LoopTask, this, 0, 1000, "令牌客户"); + _task = Sys.AddTask(&LinkClient::LoopTask, this, 0, 1000, "物联客户"); // 启动时记为最后一次活跃接收 LastActive = Sys.Ms(); @@ -55,24 +50,89 @@ void LinkClient::Close() if (!Opened) return; Sys.RemoveTask(_task); - //Sys.RemoveTask(_taskBroadcast); Opened = false; } -bool LinkClient::Send(LinkMessage& msg) { - return false; +void LinkClient::CheckNet() +{ + auto mst = Master; + + // 检测主链接 + if (!mst) + { + auto uri = Server; + // 创建连接服务器的Socket + auto socket = Socket::CreateRemote(uri); + if (!socket) return; + + Master = socket; + + debug_printf("LinkClient::CheckNet %s 成功创建主连接\r\n", socket->Host->Name); + + // 已连接时,减慢网络检查速度 + Sys.SetTaskPeriod(_task, 5000); + } + // 检测主链接是否已经断开 + else if (!mst->Host->Linked) + { + debug_printf("LinkClient::CheckNet %s断开,切换主连接\r\n", mst->Host->Name); + + delete mst; + Master = nullptr; + + Status = 0; + + // 未连接时,加快网络检查速度 + Sys.SetTaskPeriod(_task, 1000); + } } -bool LinkClient::Invoke(String& action, String& args) { +// 定时任务 +void LinkClient::LoopTask() +{ + TS("LinkClient::LoopTask"); + + // 最大不活跃时间ms,超过该时间时重启系统 + // WiFi触摸开关建议5~10分钟,网关建议5分钟 + // MaxNotActive 为零便不考虑重启 + if (MaxNotActive != 0 && LastActive + MaxNotActive < Sys.Ms()) Sys.Reboot(); + + CheckNet(); + if (!Master) return; + + // 状态。0准备、1握手完成、2登录后 + switch (Status) + { + case 0: + Login(); + + // 登录成功后,心跳一次,把数据同步上去 + Sys.Sleep(1000); + if (Status >= 2) Ping(); + + break; + + case 2: + Ping(); + break; + } +} + +bool LinkClient::Send(const LinkMessage& msg) { + return Master->Send(msg.GetString()); +} + +bool LinkClient::Invoke(const String& action, const String& args) { // 消息缓冲区,跳过头部 char cs[512]; - String str(&cs[sizeof(LinkMessage)], sizeof(cs) - sizeof(LinkMessage)); + String str(&cs[sizeof(LinkMessage)], sizeof(cs) - sizeof(LinkMessage), false); + str.SetLength(0); // 构造内容 str += "{\"action\":\""; str += action; - str += "\",\"args\":}"; + str += "\",\"args\":{"; str += args; str += "\"}"; @@ -92,3 +152,122 @@ bool LinkClient::Invoke(String& action, String& args) { // 发送 return Send(msg); } + +// 登录 +void LinkClient::Login() +{ + TS("LinkClient::Login"); + + Json json; + String args; + json.SetOut(args); + + json.Add("User", User); + + // 原始密码对盐值进行加密,得到登录密码 + auto now = DateTime::Now().TotalMs(); + auto arr = Buffer(&now, 8); + ByteArray bs; + bs = arr; + RC4::Encrypt(arr, Pass); + // 散列明文和密码连接在一起 + auto pass = bs.ToHex(); + pass += arr.ToHex(); + + json.Add("Password", pass); + + Invoke("Login", args); +} + +bool LinkClient::OnLogin(LinkMessage& msg) +{ + if (!msg.Reply) return false; + + TS("LinkClient::OnLogin"); + + msg.Show(true); + + auto str = msg.GetString(); + //Json json = str; + + if (msg.Error) + { + Status = 0; + debug_printf("登录失败!\r\n"); + } + else + { + Status = 2; + debug_printf("登录成功!\r\n"); + + // 登录成功后加大心跳间隔 + Sys.SetTaskPeriod(_task, 60000); + } + + return true; +} + +// 心跳,用于保持与对方的活动状态 +void LinkClient::Ping() +{ + TS("LinkClient::Ping"); + + if (LastActive > 0 && LastActive + 300000 < Sys.Ms()) + { + // 30秒无法联系,服务端可能已经掉线,重启Hello任务 + debug_printf("300秒无法联系,服务端可能已经掉线,重新开始握手\r\n"); + + delete Master; + Master = nullptr; + + Status = 0; + + Sys.SetTaskPeriod(_task, 5000); + + return; + } + + // 30秒内发过数据,不再发送心跳 + if (LastSend > 0 && LastSend + 60000 > Sys.Ms()) return; + + Json json; + String args; + json.SetOut(args); + + //json.Add("Data", Store.Data.ToHex()); + + // 原始密码对盐值进行加密,得到登录密码 + auto ms = (int)Sys.Ms(); + json.Add("Time", ms); + + Invoke("Ping", args); +} + +bool LinkClient::OnPing(LinkMessage& msg) +{ + TS("LinkClient::OnPing"); + + if (!msg.Reply) return false; + + msg.Show(true); + if (msg.Error) return false; + + auto str = msg.GetString(); + Json json = str; + + int ms = json["Time"].AsInt(); + int cost = (int)(Sys.Ms() - ms); + + if (Delay) + Delay = (Delay + cost) / 2; + else + Delay = cost; + + debug_printf("心跳延迟 %dms / %dms \r\n", cost, Delay); + + // 同步本地时间 + int serverTime = json["ServerTime"].AsInt(); + if (serverTime > 1000) ((TTime&)Time).SetTime(serverTime); + + return true; +} diff --git a/Link/LinkClient.h b/Link/LinkClient.h index 0257794c..35c7f7cc 100644 --- a/Link/LinkClient.h +++ b/Link/LinkClient.h @@ -21,7 +21,11 @@ public: int Delay; // 心跳延迟。一条心跳指令从发出到收到所花费的时间 int MaxNotActive; // 最大不活跃时间ms,超过该时间时重启系统。默认0 - Socket* Socket; + NetUri Server; + String User; + String Pass; + + Socket* Master; // 主链接。服务器长连接 DataStore Store; // 数据存储区 Dictionary Routes; // 路由集合 @@ -31,84 +35,32 @@ public: void Close(); // 发送消息 - bool Invoke(String& action, String& args); + bool Invoke(const String& action, const String& args); bool Reply(String& action, int code, String& result, int seq); void OnReceive(String& data); - void LocalSend(int start, const Buffer& bs); // 收到功能消息时触发 //MessageHandler Received; void* Param; - // 常用系统级消息 - // 握手广播 - void SayHello(bool broadcast); - - // 注册 - void Register(); - // 登录 void Login(); - // 重置并上报 - void Reset(const String& reason); - void Reboot(const String& reason); - // Ping指令用于保持与对方的活动状态 + // 心跳,用于保持与对方的活动状态 void Ping(); - /*void Read(int start, int size); - void Write(int start, const Buffer& bs); - void Write(int start, byte dat); - - // 异步上传并等待响应,返回实际上传字节数 - int WriteAsync(int start, const Buffer& bs, int msTimeout); - - // 必须满足 start > 0 才可以。 - void ReportAsync(int start, uint length = 1); - - // 远程调用 - void Invoke(const String& action, const Buffer& bs); - - // 远程调用委托。传入参数名值对以及结果缓冲区引用,业务失败时返回false并把错误信息放在结果缓冲区 - typedef bool(*InvokeHandler)(void* param, const Pair& args, Stream& result); - // 注册远程调用处理器 - void Register(cstring action, InvokeHandler handler, void* param = nullptr); - // 模版支持成员函数 - template - void Register(cstring action, bool(T::*func)(const Pair&, Stream&), T* target) - { - Register(action, *(InvokeHandler*)&func, target); - }*/ - static LinkClient* Current; private: - bool Send(LinkMessage& msg); - - // 跳转 - /*bool OnRedirect(IDictionary); + bool Send(const LinkMessage& msg); - bool OnLogin(TokenMessage& msg, TokenController* ctrl); - - bool OnPing(TokenMessage& msg, TokenController* ctrl); - - bool ChangeIPEndPoint(const NetUri& uri); - - void OnRead(const TokenMessage& msg, TokenController* ctrl); - void OnWrite(const TokenMessage& msg, TokenController* ctrl); - - void OnInvoke(const TokenMessage& msg, TokenController* ctrl); - bool OnInvoke(const String& action, const Pair& args, Stream& result);*/ + bool OnPing(LinkMessage& msg); + bool OnLogin(LinkMessage& msg); private: uint _task; - void* _Expect; // 等待内容 - - int NextReport = -1; // 下次上报偏移,-1不动 - byte ReportLength; // 下次上报数据长度 void LoopTask(); - bool CheckReport(); void CheckNet(); }; diff --git a/Link/LinkMessage.cpp b/Link/LinkMessage.cpp index 9e5f5977..8b52206d 100644 --- a/Link/LinkMessage.cpp +++ b/Link/LinkMessage.cpp @@ -13,5 +13,11 @@ void LinkMessage::Init() { *(byte*)Reply = 0; Seq = 0; + Code = 0; Length = 0; } + +void LinkMessage::Show(bool newline) const { + String str((cstring)&this[1], Length); + str.Show(newline); +} diff --git a/Link/LinkMessage.h b/Link/LinkMessage.h index 12dd5d5c..c44c80f8 100644 --- a/Link/LinkMessage.h +++ b/Link/LinkMessage.h @@ -18,9 +18,12 @@ public: // 数据指针 const void* Data() const { return (const void*)&this[1]; } + const String GetString() const { return String((cstring)&this[1], Length); } void Init(); + void Show(bool newline = false) const; + private: }; diff --git a/vs/SmartOS.vcxproj b/vs/SmartOS.vcxproj index 0ccdfaf2..27b7b17d 100644 --- a/vs/SmartOS.vcxproj +++ b/vs/SmartOS.vcxproj @@ -107,6 +107,7 @@ + diff --git a/vs/SmartOS.vcxproj.filters b/vs/SmartOS.vcxproj.filters index aa8c0052..cf3b64df 100644 --- a/vs/SmartOS.vcxproj.filters +++ b/vs/SmartOS.vcxproj.filters @@ -587,5 +587,8 @@ Link + + Link + \ No newline at end of file