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