using System; using System.Collections.Generic; using System.Net.Sockets; using System.Text; using System.Threading; using System.Timers; using HDL_ON; using HDL_ON.Entity; using HDL_ON.UI; namespace HDL_ON.DriverLayer { /// /// link网关本地链接逻辑 /// public class Control_Tcp { #region ■ 变量声明___________________________ /// /// link网关链接逻辑 /// private static Control_Tcp m_Current = null; /// /// link网关链接逻辑 /// public static Control_Tcp Current { get { if (m_Current == null) { m_Current = new Control_Tcp(); } return m_Current; } } /// /// Link网关tcp接收回调,理论上它不会处理zigbee的上报(第一个参数是网关的mac,第二个参数是接收的数据) /// private Action tcpReceiveEvent = null; /// /// Tcp客户端连接池 /// private List listTcpClient = new List(); /// /// 是否搜索网关 /// private bool isSearchGateway = false; #endregion #region ■ 搜索Link网关_______________________ // /// // /// 搜索Link网关 // /// // /// 搜索到网关的回调函数,-1:异常了 1:收到一个A网关 // /// 搜索时间(秒) // /// 当搜索到网关时,是否只连接本地的(不能瞎连其他的网关,但是网关搜索界面特殊) // public void SearchLinkGateway(Action resultEvent, int searchTime, bool connectLocalOnly) // { // this.isSearchGateway = true; // //算了,在这里启动远程连接吧,反正也就执行一次 // HdlLinkCloudConnectLogic.Current.StartCloudMqttConnection(); // var listGateway = HdlLinkGatewayLogic.Current.GetAllLocalGateway(); // var dicGateway = new Dictionary(); // foreach (var gw in listGateway) // { // //获取本地全部的A网关的mac // dicGateway[gw.GwMac] = gw; // } // //重复检测 // var listCheck = new HashSet(); // var sendData = "Topic:/user/all/custom/gateway/search\r\n"; // var dic = new Dictionary(); // dic["id"] = HdlGatewayResourse.AGatewayLinkFlage; // dic["time_stamp"] = HdlHttpLogic.Current.GetTimestamp().PadRight(13, '0'); // var bodyData = Newtonsoft.Json.JsonConvert.SerializeObject(dic); // sendData += "Length:" + bodyData.Length + "\r\n\r\n"; // sendData += bodyData; // //初始化udp // var udpLogic = new HdlUdpLogic(); // if (udpLogic.InitUdp("239.0.168.188", 8585) == false) // { // //关闭udp // udpLogic.CloseUdp(); // resultEvent?.Invoke(-1, null); // return; // } // HDL_ON.DriverLayer.Control.Ins.OpenUdp(8585); // //接收事件 // Action actionEvent = (ipPoint, result) => // { // //处理搜索A网关时,网关回复的数据 // try // { // ///// // System.Net.IPAddress s = ipPoint.Address; // MainPage.Log("TcpClient->SearchLinkGateway", $"{s}==网关搜索本地回复,数据内容{result}"); // //检验A协议网关主题(如果检验是指定主题,则返回负载数据,否则返回null) // var resultData = HdlLinkGatewaySendLogic.Current.CheckLinkGatewayTopic("/user/all/custom/gateway/search_reply", result); // if (resultData == null) { return; } // //调用回调函数 // var info = Newtonsoft.Json.JsonConvert.DeserializeObject(resultData); // if (info == null) // { // //数据异常,退出 // return; // } // if (!(string.IsNullOrEmpty(info.HomeId) == true || info.HomeId == Common.Config.Instance.HomeId)) // { // //网关已经绑定其他住宅,退出 // return; // } // ///tcp连接目标设备的ip // info.Ip_address = ipPoint.Address.ToString(); // //搜索到的这个网关,是否是存在在本地了的 // if (dicGateway.ContainsKey(info.Device_mac) == true) // { // var gateWay = dicGateway[info.Device_mac]; // //如果搜索得到本地存在的网关,则使用本地通信 // gateWay.OnlineStatu = 1; // gateWay.isLocalEncrypt = info.isLocalEncrypt; // gateWay.Master = info.Master; // //gateWay.Slaver_list1 = info.Slaver_list1; // gateWay.Oid = info.Oid; // gateWay.InGatewayHomeId = info.HomeId; // info.IsBindGateway = true; // Common.Config.Instance.Home.NowHomeOnlineStatu = "1"; // } // //搜索到的网关,建立链接 // if (connectLocalOnly == false || info.IsBindGateway == true) // { // this.ConnectLocalLinkGateway(info.Device_mac, info.Ip_address, info.GatewayId, info.isLocalEncrypt); // } // else // { // //获取连接对象 // var connectObj = this.GetConnectObject(info.Device_mac, false); // if (connectObj != null) // { // //如果有这个对象, 则替换一下它的网关id // connectObj.GatewayId = info.GatewayId; // connectObj.localEncrypt = info.isLocalEncrypt; // return; // } // } // if (listCheck.Contains(info.Device_mac) == true) // { // //已经处理过了 // return; // } // listCheck.Add(info.Device_mac); //#if DEBUG // Console.WriteLine($"搜索网关捕抓到网关:{info.Device_mac}"); //#endif // //info // //回调函数 // resultEvent?.Invoke(1, info); // } // catch (Exception e) // { // string str = e.Message; // } // }; // udpLogic.ReceviceEvent += actionEvent; // var dateTime = System.DateTime.Now.AddSeconds(searchTime); // while (this.isSearchGateway && System.DateTime.Now < dateTime) // { // udpLogic.SendData(sendData, "239.0.168.188", 8585); // udpLogic.SendData(sendData, "255.255.255.255", 8585); // System.Threading.Thread.Sleep(1000); // } // udpLogic.ReceviceEvent -= actionEvent; // //别关那么快 // System.Threading.Thread.Sleep(300); // this.isSearchGateway = false; // //关闭udp // udpLogic.CloseUdp(); // } // /// // /// 停止搜索Link网关 // /// // public void StopSearchLinkGateway() // { // this.isSearchGateway = false; // } #endregion #region ■ 链接本地Link网关___________________ /// /// 链接Link网关 /// /// 网关mac /// 网关ip /// 网关Id /// 该tcp所指向的网关,是否是当前住宅所绑定的 /// public void ConnectLocalLinkGateway(string i_mac, string i_ipAdrr, string i_gwId, bool localEncrypt) { if (i_mac == string.Empty || i_ipAdrr == string.Empty) { return; } var connectData = new TcpConnectData(); connectData.GatewayId = i_gwId; connectData.GatewayIp = i_ipAdrr; connectData.GatewayMac = i_mac; connectData.localEncrypt = localEncrypt; lock (this.listTcpClient) { //找到当前网关的连接 if (this.listTcpClient.Find((v) => v.GatewayMac == i_mac) != null) { return; } this.listTcpClient.Add(connectData); } try { //TCP连接 connectData.tcpClient = new Communication.TcpClient(i_ipAdrr, 8586); connectData.tcpClient.Connect(); connectData.tcpClient.ReadFormSocket(); connectData.tcpClient.ExitEvent += (s, e) => { StopConnectLinkGateway(connectData); }; connectData.tcpClient.ReceiveBytesAction += (topic, bytes, socket) => { managerLinkGatewayData(topic, bytes, socket); }; } catch { #if DEBUG //HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, i_ipAdrr + "Tcp链接不了"); #endif StopConnectLinkGateway(connectData); } } public TcpConnectData GetConnectObjectBySocket(Socket socket) { lock (listTcpClient) { var tcpConnectData = listTcpClient.Find(tcd => tcd.tcpClient._socket == socket); return tcpConnectData; } } /// /// 断开全部的Link网关连接 /// public void StopAllConnectLinkGateway() { var listTemp = new List(); for (int i = 0; i < this.listTcpClient.Count; i++) { //创另外一个对象出来,是有作用的 listTemp.Add(this.listTcpClient[i]); } foreach (var data in listTemp) { //断开指定的link网关连接 this.StopConnectLinkGateway(data); } //清空缓存 this.listTcpClient.Clear(); } /// /// 断开指定的link网关连接 /// /// 需要断开连接的网关mac public void StopConnectLinkGateway(string mac) { var tempGateway = listTcpClient.Find((v) => v.GatewayMac == mac); if (tempGateway != null) { //断开指定的link网关连接 this.StopConnectLinkGateway(tempGateway); } } /// /// 断开指定的link网关连接 /// /// 连接数据 private void StopConnectLinkGateway(TcpConnectData connectData) { lock (listTcpClient) { this.listTcpClient.Remove(connectData); connectData.tcpClient.Dispose(); } } #endregion #region ■ 发送数据到Link网关_________________ /// /// 发送数据到本地Link网关 /// /// 链接信息 /// 发送的数据 /// private void DoSendDataToLinkGateway(TcpConnectData connectData, string sendData) { #if DEBUG MainPage.Log(DateTime.Now + ":TcpClient->DoSendDataToLinkGateway", $"本地发送数据,Data:{sendData}"); #endif byte[] byteData; //44bytes 加密数据 AES加密 base64密文 ////目前默认都是加密载荷 //byteData = this.AesEncryptPayload(byteData, Config.Instance.Home.localSecret); //解密负载数据(因为写密钥给网关一定明文,因为那时网关还没有密钥) if (connectData.localEncrypt) { var headerAndBody = sendData.Split("\r\n\r\n"); string topic = headerAndBody[0].Split("\r\n")[0]; //加密载荷 var byteBody = this.AesEncryptPayload(System.Text.Encoding.UTF8.GetBytes(headerAndBody[1]), DB_ResidenceData.Instance.CurrentRegion.localSecret); var header = System.Text.Encoding.UTF8.GetBytes($"{topic}\r\nLength:{byteBody.Length}\r\n" + "\r\n"); byteData = new byte[header.Length + byteBody.Length]; System.Array.Copy(header, 0, byteData, 0, header.Length); ///参数(源文件,取源文件索引值,存储器,存放位置,源文件长度) System.Array.Copy(byteBody, 0, byteData, header.Length, byteBody.Length); } else { byteData = Encoding.UTF8.GetBytes(sendData); } //byteData = Encoding.UTF8.GetBytes(sendData); //将数据写入网络流 connectData.tcpClient?.CommSend(byteData, byteData.Length); } #endregion #region ■ 处理Link网关的数据_________________ /// /// 处理接收本地Link网关的数据(返回值 0:还不存在完成的数据 1:已经存在完成的数据) /// /// 数据 private void managerLinkGatewayData(string topic, byte[] bytes, Socket socket) { try { var arryTopic = topic.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries); if (arryTopic.Length < 2) { //非正规主题 return; } var connectData = this.GetConnectObjectBySocket(socket); if (connectData == null) { return; } string gatewayMac = connectData.GatewayMac; string gatewayId = connectData.GatewayId; ///解密负载数据 if (connectData.localEncrypt == true) { var tempBytes = bytes; bytes = this.AesDecryptPayload(bytes, DB_ResidenceData.Instance.CurrentRegion.localSecret); if (bytes == null) { bytes = tempBytes; } } /////目前网关默认都是加密的 //bytes = this.AesDecryptPayload(bytes, Config.Instance.Home.localSecret); var bodyData = System.Text.Encoding.UTF8.GetString(bytes); MainPage.Log(DateTime.Now + ":Control_Tcp->本地接收数据", $"Topic:{topic}\r\nData:{bodyData}"); if (topic.Contains("thing/property/up") == true) { //这个是Link设备的状态上报,别整,只要zigbee的 var temp = Newtonsoft.Json.JsonConvert.DeserializeObject(bodyData); return; } } catch (Exception e) { } } #endregion #region ■ 结构体_____________________________ /// /// Tcp连接信息 /// public class TcpConnectData { /// /// 网关的mac(主键使用) /// public string GatewayMac = string.Empty; /// /// 网关ip /// public string GatewayIp = string.Empty; /// /// 网关Id(不是云端的那个) /// public string GatewayId = string.Empty; // public Communication.TcpClient tcpClient; /// /// 本地是否加密 /// public bool localEncrypt; } #endregion #region ■ 加密以及解密_______________________ /// /// 加密负载为二进制流(Link专用) /// /// /// /// private byte[] AesEncryptPayload(byte[] toEncryptArray, string key) { //加密密钥为空 if (key == string.Empty) { return toEncryptArray; } try { if (string.IsNullOrEmpty(key)) return toEncryptArray; //配置AES加密Key(密钥、向量、模式、填充) var rm = new System.Security.Cryptography.RijndaelManaged { Key = Encoding.UTF8.GetBytes(key), IV = Encoding.UTF8.GetBytes(key), Mode = System.Security.Cryptography.CipherMode.CBC, Padding = System.Security.Cryptography.PaddingMode.PKCS7 }; //创建AES加密器对象 var cTransform = rm.CreateEncryptor(); //使用AES将明文流转成密文字节数组 return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); } catch { return null; } } /// /// 采用Aes解密负载数据(Link专用) /// /// /// /// private byte[] AesDecryptPayload(byte[] toEncryptArray, string key) { //解密密钥为空 if (key == string.Empty) { return toEncryptArray; } try { //配置AES加密Key(密钥、向量、模式、填充) var rm = new System.Security.Cryptography.RijndaelManaged { Key = Encoding.UTF8.GetBytes(key), IV = Encoding.UTF8.GetBytes(key), Mode = System.Security.Cryptography.CipherMode.CBC, Padding = System.Security.Cryptography.PaddingMode.PKCS7 }; //创建AES解密器对象 var cTransform = rm.CreateDecryptor(); //使用AES将密文流转成明文的字节数组 return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length); } catch { return null; } } #endregion } } namespace Communication { //TCP客户端实现 public class TcpClient { private const int MAX_RECONNECT_TIMES = 3; //断线重连尝试次数 //soket对象及参数 public Socket _socket; private string host; private int port; private bool reconnect; private bool run = true; private int ConnecteFailedCount { get; set; } public int ReconnectStatistics { get; private set; } private static uint _keepAliveTime = 5000; //无数据交互持续时间(ms) private static uint _keepAliveInterval = 500; //发送探测包间隔(ms) //重连失败事件 public event EventHandler ExitEvent; public Action ReceiveBytesAction; //构造函数 public TcpClient(string host, int port) { this.host = host; this.port = port; reconnect = false; ConnecteFailedCount = 0; } //连接 public void Connect() { byteList = new List(1024 * 5); _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Connect(host, port); MainPage.Log("TcpClient->Reconnect", $"连接成功,IP:{host}"); ConnecteFailedCount = MAX_RECONNECT_TIMES; //设置KeepAlive _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); byte[] optionValue = new byte[12]; BitConverter.GetBytes(1).CopyTo(optionValue, 0); BitConverter.GetBytes(_keepAliveTime).CopyTo(optionValue, 4); BitConverter.GetBytes(_keepAliveInterval).CopyTo(optionValue, 8); _socket.IOControl(IOControlCode.KeepAliveValues, optionValue, null); } //重连 public bool Reconnect() { ReconnectStatistics++; reconnect = false; close(); try { MainPage.Log("TcpClient->Reconnect", $"重新开始连接,IP:{host}"); Connect(); } catch (Exception e) { ConnecteFailedCount--; if (ConnecteFailedCount > 0) { //Console.WriteLine("重试次数剩余{0}",ConnecteFailedCount); reconnect = true; return true; } else { //重连失败事件 return false; } } return true; } /// /// 关闭 /// private void close() { try { MainPage.Log("TcpClient->Close", $"Socket 关闭,IP:{host}"); _socket.Close(); } catch { } } /// /// 释放资源 /// public void Dispose() { run = false; close(); } //发送数据接收实现,断线重连 public int CommSend(byte[] buffer, int size) { int sendSize = 0; try { if (_socket.Connected) { sendSize = _socket.Send(buffer, size, SocketFlags.None); } } catch (Exception e) { MainPage.Log("TcpClient->CommSend", $"发送失败,Data:{System.Text.Encoding.UTF8.GetString(buffer)} Exception:{e.Message}"); ReconnectStatistics++; reconnect = true; } return sendSize; } List byteList = new List(); //接收数据线程,使用阻塞方式接收数据 public void ReadFormSocket() { new System.Threading.Thread(() => { int count; byte[] byteBuffer = new byte[2048]; while (run) { try { try { count = _socket.Receive(byteBuffer, SocketFlags.None); for (int i = 0; i < count; i++) { byteList.Add(byteBuffer[i]); } } catch (Exception e) { count = 0; } //网络断开Receive返回0 if (count == 0 || reconnect == true) { if (run) { //重连次数用尽,退出 if (Reconnect() == false) { //通知其它功能释放及删除当前对象 ExitEvent?.Invoke(null, new EventArgs()); break; } } else { ExitEvent?.Invoke(null, new EventArgs()); } } else { while (true) { var bytes = byteList.ToArray(); var topMsgs = System.Text.Encoding.UTF8.GetString(bytes).Split("\r\n"); var lenght = getLenght(topMsgs); if (lenght <= 0) { //头部数据还没有接收完成 break; } //是否已经获取完整所有的数据 var data = new byte[lenght]; int index = getDataIndex(byteList); if (byteList.Count < index + lenght) { //当前数据还没有接收完成 break; } //复制出body数据 System.Array.Copy(bytes, index, data, 0, data.Length); //保留剩余的数据,以次用 byteList.Clear(); for (int i = index + lenght; i < bytes.Length; i++) { byteList.Add(bytes[i]); } var topic = getTopic(topMsgs); ReceiveBytesAction?.Invoke(topic, data, _socket); } } } catch (Exception e) { MainPage.Log("TcpClient->ReadFormSocket", $"Exception:{e.Message}"); } } }) { IsBackground = true }.Start(); } /// /// 获取内容长度 /// /// /// int getLenght(string[] topMsgs) { for (int i = 0; i < topMsgs.Length; i++) { string topMsg = topMsgs[i].Trim(); if (topMsg.StartsWith("Length:")) { return int.Parse(topMsg.Replace("Length:", "")); } } //找不到长度 return -1; } /// /// 获取主题 /// /// /// private string getTopic(string[] topMsgs) { for (int i = 0; i < topMsgs.Length; i++) { var topMsg = topMsgs[i].Trim(); if (topMsg.StartsWith("Topic:")) { return topMsg.Replace("Topic:", ""); } } //找不到主题 return null; } /** * 获取数据的开始位置 * @param arrayList 接收到的所有数据 * @return 数据位的开始索引 */ int getDataIndex(List arrayList) { var r = (byte)'\r'; var n = (byte)'\n'; for (int i = 0; i < arrayList.Count; i++) { //找出数据内容前面的两个换行 if (3 <= i && arrayList[i - 3] == r && arrayList[i - 2] == n && arrayList[i - 1] == r && arrayList[i] == n) { //剩余的数据 return i + 1; } } return -1; } } }