using System; using System.Collections.Generic; using System.Data; using global::System.IO; using System.Linq; using global::System.Net; using global::Newtonsoft.Json.Linq; namespace Pilz.Networking { public abstract class ConnectionManagerBase { private const int HEADER_LENGTH = 12; private bool listening = false; private readonly Dictionary> dicData = new Dictionary>(); public int Port { get; private set; } public bool UseAssemblyQualifiedName { get; set; } = false; public event RetriveDataEventHandler RetriveData; public delegate void RetriveDataEventHandler(ConnectionManagerBase manager, string senderIP, string cmd, object content); public bool IsListening { get { return listening; } protected set { listening = value; } } public ConnectionManagerBase(int port) { Port = port; } ~ConnectionManagerBase() { Stop(); } public abstract void Start(); public abstract void Stop(); protected abstract void SendData(IPEndPoint endPoint, byte[] data); protected abstract int GetBufferSize(); public virtual void Send(string empfängerIP, string cmd) { Send(empfängerIP, cmd, string.Empty); } public virtual void Send(string empfängerIP, string cmd, string info) { Send(empfängerIP, cmd, info); } private void RaiseRetriveData(string senderIP, string cmd, object content) { RetriveData?.Invoke(this, senderIP, cmd, content); } protected void ProcessRetrivedData(string senderIP, byte[] buf) { int readInteger(int index) => buf[index] << 24 | buf[index + 1] << 16 | buf[index + 2] << 8 | buf[index + 3]; int dataID = readInteger(0); int packageID = readInteger(4); int packageCount = readInteger(8); bool resolveData = true; // Remember data var data = buf.Skip(HEADER_LENGTH).ToArray(); Dictionary dicMyData; if (dicData.ContainsKey(dataID)) { dicMyData = dicData[dataID]; if (dicMyData.ContainsKey(packageID)) { dicMyData[packageID] = data; } else { dicMyData.Add(packageID, data); } } else { dicMyData = new Dictionary() { { packageID, data } }; dicData.Add(dataID, dicMyData); } if (dicMyData.Count < packageCount) { resolveData = false; } // Resolve Data if (resolveData) { if (dicMyData is null) { dicMyData = dicData[dataID]; } var myData = new List(); foreach (var kvp in dicMyData.OrderBy(n => n.Key)) myData.AddRange(kvp.Value); dicMyData.Remove(dataID); object content = null; string cmd = string.Empty; try { var res = DecodeFromBytes(myData.ToArray()); cmd = res.cmd; content = res.content; } catch (Exception ex) { } RaiseRetriveData(senderIP, cmd, content); } } private Random send_Rnd = new Random(); public void Send(string empfängerIP, string cmd, object content) { var ep = new IPEndPoint(NetworkFeatures.GetIPFromHost(empfängerIP).MapToIPv4(), Port); var finalBuffer = new List(); int maxBufferSize = GetBufferSize(); int maxDataSize = maxBufferSize - HEADER_LENGTH; var data = EncodeToBytes(cmd, content, UseAssemblyQualifiedName); int dataID = send_Rnd.Next(); // Some methods for later user void send() => SendData(ep, finalBuffer.ToArray()); void addInteger(int value) => finalBuffer.AddRange(new byte[] { (byte)((value >> 24) & 0xFF), (byte)((value >> 16) & 0xFF), (byte)((value >> 8) & 0xFF), (byte)(value & 0xFF) }); void addHeader(int packageID, int packagesCount) { addInteger(dataID); // Data ID addInteger(packageID); // Package ID addInteger(packagesCount); // Packages Count }; // Send data (this if statement and else content might be useless) if (data.Length > maxDataSize) { int curIndex = 0; int curID = 0; int packagesCount = (int)Math.Ceiling(data.Length / (double)maxDataSize); while (curIndex < data.Length) { finalBuffer.Clear(); addHeader(curID, packagesCount); for (int i = 1, loopTo = maxDataSize; i <= loopTo; i++) { if (curIndex < data.Length) { finalBuffer.Add(data[curIndex]); curIndex += 1; } } send(); curID += 1; } } else { addHeader(0, 1); finalBuffer.AddRange(data); send(); } } private static byte[] EncodeToBytes(string cmd, object content, bool useAssemblyQualifiedName) { var ms = new MemoryStream(); var bw = new BinaryWriter(ms); var obj = new JObject(); // Write header obj["Cmd"] = cmd; obj["ContentType"] = useAssemblyQualifiedName ? content?.GetType()?.AssemblyQualifiedName : content?.GetType()?.ToString(); // Content obj["Content"] = JToken.FromObject(content); // Write Json to MemoryStream bw.Write(System.Text.Encoding.Default.GetBytes(obj.ToString())); // Get Buffer Bytes var buf = ms.ToArray(); ms.Close(); return buf; } private static (string cmd, object content) DecodeFromBytes(byte[] buf) { string contentstring = System.Text.Encoding.Default.GetString(buf); object content = null; var contentobj = JObject.Parse(contentstring); string cmd = (string)contentobj["Cmd"]; string contenttypestring = (string)contentobj["ContentType"]; var contentlinq = contentobj["Content"]; if (!string.IsNullOrEmpty(contenttypestring)) { var contenttype = Type.GetType(contenttypestring); content = contentlinq.ToObject(contenttype); } return (cmd, content); } } }