using Galactic_Colors_Control_Common; using Galactic_Colors_Control_Common.Protocol; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; namespace Galactic_Colors_Control { /// /// Client CrossPlatform Core /// public class Client { private Socket ClientSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); public string IP = null; //Server IP public int PORT = 0; //Server Port public struct CoreConfig { public int resultsBuffer; //Max amount of waiting results public int timeout; //Request timeout in ms public int refresh; //Threads sleep in ms public CoreConfig(int buffer, int time, int speed) { resultsBuffer = buffer; timeout = time; refresh = speed; } } public CoreConfig config = new CoreConfig(20, 2000, 200); //Set default config private int _errorCount = 0; //Leave if > 5 private bool _run = true; //Thread Stop public bool isRunning { get { return _run; } } private int RequestId = 0; private List Results = new List(); private Thread RecieveThread; //Main Thread public EventHandler OnEvent; //Execute on EventData reception (must be short or async) /// /// Soft Server Reset /// public void ResetHost() { IP = null; PORT = 0; } /// /// Test and Convert Hostname to Address /// /// Hostname /// Address(IP:PORT) or Error(*'text') public string ValidateHost(string text) { if (text == null) { text = ""; } //Prevent NullException string[] parts = text.Split(new char[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries); //Split IP and Port if (parts.Length == 0) //Default config (localhost) { parts = new string[] { "" }; PORT = 25001; } else { if (parts.Length > 1) { if (!int.TryParse(parts[1], out PORT)) { PORT = 0; } //Check Port if (PORT < 0 || PORT > 65535) { PORT = 0; } } else { PORT = 25001; } } if (PORT != 0) { try { IPHostEntry ipHostEntry = Dns.GetHostEntry(parts[0]);//Resolve Hostname IPAddress host = ipHostEntry.AddressList.First(a => a.AddressFamily == AddressFamily.InterNetwork);//Get IPv4 IP = host.ToString(); return IP + ":" + PORT; } catch (Exception e) { PORT = 0; return "*" + e.Message; } } else { return "*Port Format"; } } /// /// Start Server connection /// /// Setup IP and Port before /// connection success public bool ConnectHost() { int attempts = 0; while (!ClientSocket.Connected && attempts < 5) { try { attempts++; ClientSocket.Connect(IP, PORT); } catch (SocketException) { } } if (attempts < 5) //Connection success { _run = true; RecieveThread = new Thread(ReceiveLoop); //Starting Main Thread RecieveThread.Start(); return true; } else { ResetSocket(); return false; } } /// /// Hard Reset (unsafe) /// private void ResetSocket() { ClientSocket.Close(); ClientSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } /// /// Close socket and exit program. /// public void ExitHost() { try { Send(new RequestData(GetRequestId(), new string[1] { "exit" })); } catch { }// Tell the server we are exiting _run = false; //Stopping Thread RecieveThread.Join(2000); ClientSocket.Shutdown(SocketShutdown.Both); ClientSocket.Close(); ResetHost(); } /// /// Send RequestData to server /// /// Request args /// ResultData or Timeout public ResultData Request(string[] args) { switch(args[0]) { case "exit": ExitHost(); return new ResultData(GetRequestId(), ResultTypes.OK); case "ping": return PingHost(); default: return Execute(args); } } /// /// Send row command to server /// private ResultData Execute(string[] args) { RequestData req = new RequestData(GetRequestId(), args); if (!Send(req)) return new ResultData(req.id, ResultTypes.Error, Common.Strings("Send Exception")); DateTime timeoutDate = DateTime.Now.AddMilliseconds(config.timeout); //Create timeout DataTime while (timeoutDate > DateTime.Now) { foreach (ResultData res in Results.ToArray()) //Check all results { if (res.id == req.id) { Results.Remove(res); return res; } } Thread.Sleep(config.refresh); } return new ResultData(req.id, ResultTypes.Error, Common.Strings("Timeout")); } /// /// Ping Current Server IP /// /// Time in ms or 'Timeout' private ResultData PingHost() { Ping ping = new Ping(); PingReply reply; reply = ping.Send(IP); if (reply.Status == IPStatus.Success) return new ResultData(GetRequestId(), ResultTypes.OK, Common.SplitArgs(reply.RoundtripTime.ToString() + "ms")); return new ResultData(GetRequestId(), ResultTypes.Error, Common.SplitArgs("Timeout")); } /// /// Send Data object to server /// /// Send success private bool Send(Data packet) { try { ClientSocket.Send(packet.ToBytes()); return true; } catch //Can't contact server { _errorCount++; } return false; } /// /// Main Thread /// private void ReceiveLoop() { while (_run) { var buffer = new byte[2048]; int received = 0; try { received = ClientSocket.Receive(buffer, SocketFlags.None); } catch { _errorCount++; } if (_errorCount >= 5) { _run = false; } if (received == 0) return; _errorCount = 0; var data = new byte[received]; Array.Copy(buffer, data, received); Data packet = Data.FromBytes(ref data); //Create Data object from recieve bytes if (packet != null) { switch (packet.GetType().Name) { case "EventData": EventData eve = (EventData)packet; if (OnEvent != null) OnEvent.Invoke(this, new EventDataArgs(eve)); break; case "ResultData": ResultData res = (ResultData)packet; ResultAdd(res); break; default: //Ignore others Packets break; } } Thread.Sleep(config.refresh); } //TODOOutput.Add("/*exit*/"); } public int GetRequestId(bool indent = true) { if (indent) { RequestId++; } return RequestId; } /// /// Add to Results /// public void ResultAdd(ResultData res) { while (Results.Count + 1 > config.resultsBuffer) { Results.RemoveAt(0); } //Removes firsts Results.Add(res); } } }