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;
}
}
}