From 5ae152f203f8b05da58fbd02bfc4dca5583ae3c8 Mon Sep 17 00:00:00 2001 From: Herry Pattar Date: Mon, 16 Mar 2015 20:51:35 +0300 Subject: [PATCH] [v.0.03a] Test Code and And Minor Update SUMMARY: * Added Test Code for Server and Network Client * Implemented NetworkClient ( early to call it final, but it's working and usable right now ) * Implemented basic shutdown and start application algorithms --------------------------------- *OLD!* AutoClient: --------------------------------- * Renamed AutoClient into NetworkClient * Added basic functionality and a test temporary code --------------------------------- App: --------------------------------- * Changes: Added code for Shutdown() function. Overrided basic Shutdown() function. Changed ShutdownMode to Manual Added code for testing Serialization and Desirialization of UserInfo class objects. Added code for testing Server and Client class objects --------------------------------- ManualWindow: --------------------------------- Added code for testing (ping) --- AutoShare/App.config | 6 + AutoShare/App.xaml | 1 + AutoShare/App.xaml.cs | 73 +++++++- AutoShare/AutoShare.csproj | 2 +- AutoShare/Engine/Network/AutoClient.cs | 46 ----- AutoShare/Engine/Network/NetworkClient.cs | 173 +++++++++++++++++++ AutoShare/Engine/Network/NetworkServer.cs | 75 ++++++-- AutoShare/Engine/Network/Sharing/UserInfo.cs | 12 +- AutoShare/MainWindow.xaml.cs | 18 +- AutoShare/Properties/Settings.Designer.cs | 21 +++ AutoShare/Properties/Settings.settings | 6 + 11 files changed, 362 insertions(+), 71 deletions(-) delete mode 100644 AutoShare/Engine/Network/AutoClient.cs create mode 100644 AutoShare/Engine/Network/NetworkClient.cs diff --git a/AutoShare/App.config b/AutoShare/App.config index 577b805..159dd34 100644 --- a/AutoShare/App.config +++ b/AutoShare/App.config @@ -23,6 +23,9 @@ userlist + + 12344 + @@ -38,6 +41,9 @@ True + + users.temp + \ No newline at end of file diff --git a/AutoShare/App.xaml b/AutoShare/App.xaml index 2f8e194..fcab8d9 100644 --- a/AutoShare/App.xaml +++ b/AutoShare/App.xaml @@ -1,6 +1,7 @@  diff --git a/AutoShare/App.xaml.cs b/AutoShare/App.xaml.cs index 3d0c9de..ea1e283 100644 --- a/AutoShare/App.xaml.cs +++ b/AutoShare/App.xaml.cs @@ -9,7 +9,9 @@ using System.IO; using System.Windows.Shapes; using System.Security.Permissions; - +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Serialization; +using AutoShare.Engine.Network.Sharing; namespace AutoShare { @@ -19,14 +21,25 @@ namespace AutoShare public partial class App : Application { public AutoShare.Engine.IO.FilesFolderChecker FolderWatchdog; - public AutoShare.Engine.IO.SerializableList KnownUsers; - + public AutoShare.Engine.IO.SerializableList KnownUsers; + public AutoShare.Engine.Network.NetworkClient Client; + public AutoShare.Engine.Network.NetworkServer Server; //TEMP public bool AskUserExit = true; App() { #region Initialization Block + KnownUsers = new Engine.IO.SerializableList(); + /** UNCOMMENT TO CREATE TEST USERINFO + * + * + UserInfo self = new UserInfo("TestBuddy", "Apple", new System.Net.IPEndPoint(new System.Net.IPAddress(new byte[4]{127,0,0,1}),4321), true); + KnownUsers.List.Add(self); + */ + Client = new Engine.Network.NetworkClient(); + Server = new Engine.Network.NetworkServer(4321); + string path; if (AutoShare.Properties.Settings.Default.UseDocFolder) path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + System.IO.Path.DirectorySeparatorChar + AutoShare.Properties.Settings.Default.FolderPath; @@ -35,21 +48,71 @@ public partial class App : Application FolderWatchdog = new Engine.IO.FilesFolderChecker(path, true); - KnownUsers = new Engine.IO.SerializableList(); + #endregion #region Subscription to events #endregion + #region Network Startup + + + #endregion + this.Initialize(); + + + //TEMP just for testing purposes + + + + } + public void Initialize() + { + //TEMP as the Initialize() function is described in the paper + BinaryFormatter formatter = new BinaryFormatter(); + string Path = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + System.IO.Path.DirectorySeparatorChar + + AutoShare.Properties.Settings.Default.UserlistPrefix + System.Environment.UserName + '.' + AutoShare.Properties.Settings.Default.UserlistExtension; + using (FileStream tfs = new FileStream(Path, FileMode.Open, FileAccess.Read)) + KnownUsers = (AutoShare.Engine.IO.SerializableList)formatter.Deserialize(tfs); + } - public void Shutdown() + public new void Shutdown() { //save all settings AutoShare.Properties.Settings.Default.Save(); //save folder state //save user state + //TEMP (because user can change its name, need to make login system) + string CurrentUserName = System.Environment.UserName; + //filepath + string Path = AutoShare.Properties.Settings.Default.UserlistPrefix + CurrentUserName + '.' + AutoShare.Properties.Settings.Default.UserlistExtension; + string TempPath = AutoShare.Properties.Settings.Default.TemporaryUserlistName + '.' + AutoShare.Properties.Settings.Default.UserlistExtension; + //get save path + BinaryFormatter formatter = new BinaryFormatter(); + //TEMP as can be failed/access denied/program halted during I/O operation etc. Need to write first in a temp file, then rewrite, then delete temp file. + //1. Save to temp folder + using( FileStream tfs = new FileStream(TempPath, FileMode.Create, FileAccess.Write) ) + formatter.Serialize(tfs, KnownUsers, + new System.Runtime.Remoting.Messaging.Header[1]{ new System.Runtime.Remoting.Messaging.Header("USERLISTLENGTH", KnownUsers.List.Count ) } ); + //2. Overwrite + File.Copy( TempPath, System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + System.IO.Path.DirectorySeparatorChar + Path, true ); + + + //Dispose all the resources + this.Server.Dispose(); + //Finally, shutdown itself + base.Shutdown(); + } + + //END CLASS <> } + + + + + + //END APP.XAML.CS } diff --git a/AutoShare/AutoShare.csproj b/AutoShare/AutoShare.csproj index 384d941..c8ba542 100644 --- a/AutoShare/AutoShare.csproj +++ b/AutoShare/AutoShare.csproj @@ -71,7 +71,7 @@ - + diff --git a/AutoShare/Engine/Network/AutoClient.cs b/AutoShare/Engine/Network/AutoClient.cs deleted file mode 100644 index c744992..0000000 --- a/AutoShare/Engine/Network/AutoClient.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Net.Sockets; -using System.Threading; - - -namespace AutoShare.Engine.Network -{ - public class Node { - System.Net.EndPoint EndPoint; - string EncryptionKey; - } - - public class Helper - { - static int DefaultServerPort = 4321; - } - - public class Client - { - TcpClient tcp_client; - List task; - List nodes; - Client() - { - - } - void AddNode(Node node){ - - } - Node this[int i] - { - get - { - return nodes[i]; - } - } - ~Client() - { - - } - } -} diff --git a/AutoShare/Engine/Network/NetworkClient.cs b/AutoShare/Engine/Network/NetworkClient.cs new file mode 100644 index 0000000..374f631 --- /dev/null +++ b/AutoShare/Engine/Network/NetworkClient.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net.Sockets; +using System.Threading; +using AutoShare.Engine.Network.Sharing; + +namespace AutoShare.Engine.Network +{ + //TODO: MOVE LIST outside of class. To sync values from the server and from the client + + public class NetworkClient + { + #region Helper Classes + public struct UserStatus + { + public UserInfo Info; + public DateTime LastPinged; + public bool IsOnline; + + + public UserStatus(UserInfo Info, DateTime LastPinged, bool IsOnline) + { + this.Info = Info; + this.LastPinged = LastPinged; + this.IsOnline = IsOnline; + } + + } + + #endregion + #region Private Members + List task; + ManualResetEvent mres; + List socket_threads; + List nodes; //ping nodes + + #endregion + #region Thread Functions + + void SocketThread(object index) + { + TcpClient cli = null; + UserStatus us; + try { + + lock (nodes) + us = nodes[(int)index]; + cli = new TcpClient(); + //BEFORE FIRST + cli.NoDelay = false; + cli.ReceiveTimeout = 10; + cli.SendTimeout = 5; + //FIRST - connect to the last known + cli.Connect(us.Info.LastKnownAddress); + //SECOND - cycle through the known ip addresses + if (!cli.Connected) + { + for (int i = 0, s = us.Info.AddressHistory.Count; i < s; ++i) + { + cli.Connect(us.Info.AddressHistory[i].EndPoint); + if (cli.Connected) + break; + } + } + //THIRD - EITHER DIE WITH NO STATE OR CONNECT AND SYNC LIST + us.LastPinged = DateTime.Now; + //TEMP just a workaround + us.IsOnline = cli.Connected; + if (us.IsOnline) + { + //TEMP: Send something + NetworkStream ns = cli.GetStream(); + byte[] bytes = System.Text.UTF8Encoding.UTF8.GetBytes("PING"); + ns.Write( bytes, 0, bytes.Length ); + ns.Close(); + } + } + catch (ThreadAbortException) { + + } + finally { + //set thread running to false + if(cli!=null) + cli.Close(); + + } + + + + + //END + + } + + #endregion + #region Constructor and Destructors + public NetworkClient(IEnumerable UserStatuses = null, bool StartImmediately = true) + { + if (UserStatuses != null && UserStatuses.Count() > 0) + nodes = new List(UserStatuses); + else + nodes = new List(); + socket_threads = new List(); + mres = new ManualResetEvent(false); + if (nodes.Count > 0) + { + for (int i = 0, s = nodes.Count; i < s; ++i ) + AddThread(true); + } + } + + ~NetworkClient() + { + mres.Set(); + //Delay for dispose + mres.WaitOne(); + //Dispose object + mres.Dispose(); + } + + #endregion + #region Protected Methods + protected void AddThread(bool StartImmediately) + { + Thread thr = new Thread(SocketThread); + lock (this.socket_threads) + { + this.socket_threads.Add(thr); + if (StartImmediately) + thr.Start(this.socket_threads.Count - 1); + } + + } + #endregion + + #region API Calls and Public Accessors + public void AddNode(UserStatus UserStatus, bool StartImmediately = true) + { + lock (this.nodes) + { + this.nodes.Add(UserStatus); + this.AddThread(StartImmediately); + } + } + public void RemoveNode(int index) + { + this.nodes.RemoveAt(index); + this.socket_threads[index].Abort(); + this.socket_threads.RemoveAt(index); + } + public UserStatus this[int i] { get { return nodes[i]; } set { nodes[i] = value; } } + + public ThreadState NodeThreadState(int i) + { + return this.socket_threads[i].ThreadState; + } + + public void StartNodeThread(int i) + { + this.socket_threads[i].Start(); + } + + public void StopNodeThread(int i) + { + //TEMP and workaround. Recode this thing + this.socket_threads[i].Abort(); + } + #endregion + } +} diff --git a/AutoShare/Engine/Network/NetworkServer.cs b/AutoShare/Engine/Network/NetworkServer.cs index 210e068..9d4334d 100644 --- a/AutoShare/Engine/Network/NetworkServer.cs +++ b/AutoShare/Engine/Network/NetworkServer.cs @@ -9,14 +9,40 @@ namespace AutoShare.Engine.Network { - public class NetworkServer + public class NetworkServer:IDisposable { #region TcpListener Listener; // Îáúåêò, ïðèíèìàþùèé TCP-êëèåíòîâ ManualResetEvent HEvent; List ProcessingThreads; Thread ServerThread; + bool Disposed; //Ïîêàçûâàåò, ðàáî÷èé ëè îáúåêò êëàññà íà äàííûé ìîìåíò + #endregion + + + #region Protected Functions + void DisposeImplemention() + { + if (!Disposed) + { + this.HEvent.Set(); + for (int i = 0, s = ProcessingThreads.Count; i < s; ++i) + ProcessingThreads[i].Abort(); //TODO either remove this madness or make another approach as the processing thread could be running long and is NOT a critical (means important) thread + + Disposed = true; + } + + + } + + void Resurrect() + { + if (Disposed) + { + //TODO - ressurect + } + } #endregion #region Threading Functions void ProcessClientThreadingFunction(object client) @@ -33,9 +59,12 @@ void ProcessClientThreadingFunction(object client) NetworkStream ns = cli.GetStream(); byte[] bytes_to_send, buffer = new byte[NetPacket.PacketSignatureRule.MaxSignatureSize]; //Accept the signature and some data (probably) - ns.Read(buffer, 0, buffer.Length); - + int nbytes = cli.Available; + ns.Read(buffer, 0, nbytes); //TEMP + if (System.Text.UTF8Encoding.UTF8.GetString(buffer, 0, nbytes) == "PING") + this.DispatchEvent(this.PingReceived); + bytes_to_send = System.Text.UTF8Encoding.UTF8.GetBytes("PONG"); //Final Function ns.Write(bytes_to_send, 0, bytes_to_send.Length); @@ -54,19 +83,37 @@ void ServerThreadFunction() { Listener.Start(); Listener.BeginAcceptTcpClient( (IAsyncResult res)=>{ - TcpClient cli = Listener.EndAcceptTcpClient(res); - if(cli!=null){ - Thread thr = new Thread(ProcessClientThreadingFunction); - ProcessingThreads.Add(thr); - thr.Start( cli ); + try + { + TcpClient cli = Listener.EndAcceptTcpClient(res); + if (cli != null) + { + Thread thr = new Thread(ProcessClientThreadingFunction); + ProcessingThreads.Add(thr); + thr.Start(cli); + } + } + catch (ObjectDisposedException){ + //ok, socket was disposed outside the code } + }, null); this.HEvent.WaitOne(); Listener.Stop(); } #endregion - + #region Public Events and Event Dispatchers + public event EventHandler AcceptedClient; + //TEMP temporary event just for testing purposes (maybe add a META HEAD "TEST"?) + //TEST Yeah, maybe + public event EventHandler PingReceived; + protected void DispatchEvent(Delegate Event, object value = null) + { + if (Event != null) + Event.DynamicInvoke(this, value??new EventArgs()); + } + #endregion #region Constructors and Destructors //Çàïóñê Ñåðâåðà public NetworkServer(int Port, bool StartImmediately = true) @@ -82,12 +129,11 @@ public NetworkServer(int Port, bool StartImmediately = true) } + // Îñòàíîâêà ñåðâåðà ~NetworkServer() { - this.HEvent.Set(); - for (int i = 0, s = ProcessingThreads.Count; i < s; ++i ) - ProcessingThreads[i].Abort(); + this.DisposeImplemention(); } #endregion #region API Calls @@ -98,6 +144,11 @@ public void Start() ServerThread.Start(); } } + + public void Dispose() + { + this.DisposeImplemention(); + } #endregion } } \ No newline at end of file diff --git a/AutoShare/Engine/Network/Sharing/UserInfo.cs b/AutoShare/Engine/Network/Sharing/UserInfo.cs index 61bfdd4..0634e59 100644 --- a/AutoShare/Engine/Network/Sharing/UserInfo.cs +++ b/AutoShare/Engine/Network/Sharing/UserInfo.cs @@ -166,14 +166,14 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) #endregion #region Properties //Changes frequently - System.Net.IPEndPoint LastKnownAddress { get { return _addr; } } - bool StrictNATDetected { get { return _strict_nat; } } + public System.Net.IPEndPoint LastKnownAddress { get { return _addr; } } + public bool StrictNATDetected { get { return _strict_nat; } set { _strict_nat = value; } } //Almost Hardcoded - string EvaluationKey { get { return _eval_key; } } - string UserName { get { return _name; } } + public string EvaluationKey { get { return _eval_key; } } + public string UserName { get { return _name; } } //Historical - bool LogChangeIPs { get { return this._log_ips; } } - List AddressHistory { get { return this._addr_history; } } + public bool LogChangeIPs { get { return this._log_ips; } } + public List AddressHistory { get { return this._addr_history; } } #endregion #region Private methods diff --git a/AutoShare/MainWindow.xaml.cs b/AutoShare/MainWindow.xaml.cs index b243ee1..8c9e5c0 100644 --- a/AutoShare/MainWindow.xaml.cs +++ b/AutoShare/MainWindow.xaml.cs @@ -35,8 +35,23 @@ public MainWindow() UsersModel = new ObservableCollection((App.Current as App).KnownUsers.List); UsersDataGrid.ItemsSource = UsersModel; UsersModel.CollectionChanged += UsersModel_CollectionChanged; + + //TEST for testing purposes + if ((App.Current as App).KnownUsers.List.Count > 0) + { + (App.Current as App).Server.PingReceived += Server_PingReceived; + (App.Current as App).Client.AddNode(new Engine.Network.NetworkClient.UserStatus((App.Current as App).KnownUsers.List[0], DateTime.MinValue, false)); + } + } + void Server_PingReceived(object sender, EventArgs e) + { + + this.Dispatcher.BeginInvoke(new Action(() => { this.ShowMessageAsync("OLOLO", "Received from someone PING message", MessageDialogStyle.Affirmative); })); + } + + void UsersModel_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { //throw new NotImplementedException(); @@ -103,7 +118,8 @@ private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e) this.ShowBinaryDialog("There are actions preventing to close the application. Do you really want to exit?", "Exit Confirmation" , () => { this.Dispatcher.BeginInvoke(new Action(() => { this.Closing -= OnClosing; - this.Close(); + this.Close(); + (App.Current as App).Shutdown(); })); }); } diff --git a/AutoShare/Properties/Settings.Designer.cs b/AutoShare/Properties/Settings.Designer.cs index ca5df15..3d49107 100644 --- a/AutoShare/Properties/Settings.Designer.cs +++ b/AutoShare/Properties/Settings.Designer.cs @@ -61,5 +61,26 @@ public string UserlistExtension { return ((string)(this["UserlistExtension"])); } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("12344")] + public int ServerPort { + get { + return ((int)(this["ServerPort"])); + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("users.temp")] + public string TemporaryUserlistName { + get { + return ((string)(this["TemporaryUserlistName"])); + } + set { + this["TemporaryUserlistName"] = value; + } + } } } diff --git a/AutoShare/Properties/Settings.settings b/AutoShare/Properties/Settings.settings index 5730e7b..c0fc3f1 100644 --- a/AutoShare/Properties/Settings.settings +++ b/AutoShare/Properties/Settings.settings @@ -14,5 +14,11 @@ userlist + + 12344 + + + users.temp + \ No newline at end of file