private static Task WachdogInactivity() { if (!Inactivity) { Inactivity = true; return(Task.Run(() => { for (int i = Settings.Profile.TimeoutIdle; i > 0; i--) { if (!Inactivity) { goto Normal; } Task.Run(() => InactivityTimer?.Invoke(i)); Thread.Sleep(1000); } if (!Inactivity) { goto Normal; } Task.Run(() => InactivityTimer?.Invoke(0)); Task.Run(() => InactivityError?.Invoke()); Waching = false; return; Normal: Task.Run(() => InactivityTimer?.Invoke(-1)); return; })); } return(null); }
private async Task WriteInternalAsync(byte[] bytes, CancellationToken cancellationToken) { InactivityTimer?.Reset(); var totalBytesWritten = 0; try { while (totalBytesWritten < bytes.Length) { var bytesRemaining = bytes.Length - totalBytesWritten; var bytesToWrite = bytesRemaining > TcpClient.Client.SendBufferSize ? TcpClient.Client.SendBufferSize : bytesRemaining; await Stream.WriteAsync(bytes, totalBytesWritten, bytesToWrite, cancellationToken).ConfigureAwait(false); totalBytesWritten += bytesToWrite; DataWritten?.Invoke(this, new ConnectionDataEventArgs(totalBytesWritten, bytes.Length)); InactivityTimer?.Reset(); } } catch (Exception ex) when(!(ex is TimeoutException) && !(ex is OperationCanceledException)) { Disconnect($"Write error: {ex.Message}"); throw new ConnectionWriteException($"Failed to write {bytes.Length} bytes to {IPAddress}:{Port}: {ex.Message}", ex); } }
/// <summary> /// Asynchronously connects the client to the configured <see cref="IPAddress"/> and <see cref="Port"/>. /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A Task representing the asynchronous operation.</returns> /// <exception cref="InvalidOperationException"> /// Thrown when the connection is already connected, or is transitioning between states. /// </exception> /// <exception cref="TimeoutException"> /// Thrown when the time attempting to connect exceeds the configured <see cref="ConnectionOptions.ConnectTimeout"/> value. /// </exception> /// <exception cref="OperationCanceledException"> /// Thrown when <paramref name="cancellationToken"/> cancellation is requested. /// </exception> /// <exception cref="ConnectionException">Thrown when an unexpected error occurs.</exception> public async Task ConnectAsync(CancellationToken?cancellationToken = null) { if (State != ConnectionState.Pending && State != ConnectionState.Disconnected) { throw new InvalidOperationException($"Invalid attempt to connect a connected or transitioning connection (current state: {State})"); } cancellationToken = cancellationToken ?? CancellationToken.None; // create a new TCS to serve as the trigger which will throw when the CTS times out a TCS is basically a 'fake' task // that ends when the result is set programmatically. create another for cancellation via the externally provided token. var timeoutTaskCompletionSource = new TaskCompletionSource <bool>(); var cancellationTaskCompletionSource = new TaskCompletionSource <bool>(); try { ChangeState(ConnectionState.Connecting, $"Connecting to {IPAddress}:{Port}"); // create a new CTS with our desired timeout. when the timeout expires, the cancellation will fire using (var timeoutCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(Options.ConnectTimeout))) { var connectTask = TcpClient.ConnectAsync(IPAddress, Port); // register the TCS with the CTS. when the cancellation fires (due to timeout), it will set the value of the // TCS via the registered delegate, ending the 'fake' task, then bind the externally supplied CT with the same // TCS. either the timeout or the external token can now cancel the operation. using (timeoutCancellationTokenSource.Token.Register(() => timeoutTaskCompletionSource.TrySetResult(true))) using (((CancellationToken)cancellationToken).Register(() => cancellationTaskCompletionSource.TrySetResult(true))) { var completedTask = await Task.WhenAny(connectTask, timeoutTaskCompletionSource.Task, cancellationTaskCompletionSource.Task).ConfigureAwait(false); if (completedTask == timeoutTaskCompletionSource.Task) { throw new TimeoutException($"Operation timed out after {Options.ConnectTimeout} seconds"); } else if (completedTask == cancellationTaskCompletionSource.Task) { throw new OperationCanceledException("Operation cancelled."); } if (connectTask.Exception?.InnerException != null) { throw connectTask.Exception.InnerException; } } } InactivityTimer?.Start(); WatchdogTimer.Start(); Stream = TcpClient.GetStream(); ChangeState(ConnectionState.Connected, $"Connected to {IPAddress}:{Port}"); } catch (Exception ex) when(!(ex is TimeoutException) && !(ex is OperationCanceledException)) { ChangeState(ConnectionState.Disconnected, $"Connection Error: {ex.Message}"); throw new ConnectionException($"Failed to connect to {IPAddress}:{Port}: {ex.Message}", ex); } }
public void InactivityTimerShouldResetAndCallbackWhenResetIsCalled() { var timer = new InactivityTimer(this.TimerCallback); timer.ResetTimer(TimeSpan.FromMilliseconds(1)); this.timerEvent.Wait(1000); Assert.AreEqual(1, this.callBackCount, "Should have fired once."); }
/// <summary> /// Initializes a new instance of the <see cref="Connection"/> class. /// </summary> /// <param name="ipEndPoint">The remote IP endpoint of the connection.</param> /// <param name="options">The optional options for the connection.</param> /// <param name="tcpClient">The optional TcpClient instance to use.</param> public Connection(IPEndPoint ipEndPoint, ConnectionOptions options = null, ITcpClient tcpClient = null) { Id = Guid.NewGuid(); IPEndPoint = ipEndPoint; Options = options ?? new ConnectionOptions(); TcpClient = tcpClient ?? new TcpClientAdapter(new TcpClient()); // invoke the configuration delegate to allow implementing code to configure // the socket. .NET standard has a limited feature set with respect to SetSocketOptions() // and there's a vast number of possible tweaks here, so delegating to implementing code // is pretty much the only option. Options.ConfigureSocket(TcpClient.Client); WriteQueueSemaphore = new SemaphoreSlim(Options.WriteQueueSize); if (Options.InactivityTimeout > 0) { InactivityTimer = new SystemTimer() { Enabled = false, AutoReset = false, Interval = Options.InactivityTimeout, }; InactivityTimer.Elapsed += (sender, e) => { var ex = new TimeoutException($"Inactivity timeout of {Options.InactivityTimeout} milliseconds was reached"); Disconnect(ex.Message, ex); }; } WatchdogTimer = new SystemTimer() { Enabled = false, AutoReset = true, Interval = 250, }; WatchdogTimer.Elapsed += (sender, e) => { if (TcpClient == null || !TcpClient.Connected) { Disconnect("The server connection was closed unexpectedly"); } }; if (TcpClient.Connected) { State = ConnectionState.Connected; InactivityTimer?.Start(); WatchdogTimer.Start(); Stream = TcpClient.GetStream(); } }
private void StopInactivityTimer() { if (InactivityTimer == null) { return; } InactivityTimer.Stop(); InactivityTimer.Enabled = false; InactivityTimer.Dispose(); }
/// <summary> /// Initializes a new instance of the <see cref="Connection"/> class. /// </summary> /// <param name="ipEndPoint">The remote IP endpoint of the connection.</param> /// <param name="options">The optional options for the connection.</param> /// <param name="tcpClient">The optional TcpClient instance to use.</param> public Connection(IPEndPoint ipEndPoint, ConnectionOptions options = null, ITcpClient tcpClient = null) { Id = Guid.NewGuid(); IPEndPoint = ipEndPoint; Options = options ?? new ConnectionOptions(); TcpClient = tcpClient ?? new TcpClientAdapter(new TcpClient()); TcpClient.Client.ReceiveBufferSize = Options.ReadBufferSize; TcpClient.Client.SendBufferSize = Options.WriteBufferSize; if (Options.InactivityTimeout > 0) { InactivityTimer = new SystemTimer() { Enabled = false, AutoReset = false, Interval = Options.InactivityTimeout, }; InactivityTimer.Elapsed += (sender, e) => { var ex = new TimeoutException($"Inactivity timeout of {Options.InactivityTimeout} milliseconds was reached"); Disconnect(ex.Message, ex); }; } WatchdogTimer = new SystemTimer() { Enabled = false, AutoReset = true, Interval = 250, }; WatchdogTimer.Elapsed += (sender, e) => { if (TcpClient == null || !TcpClient.Connected) { Disconnect($"The server connection was closed unexpectedly"); } }; if (TcpClient.Connected) { State = ConnectionState.Connected; InactivityTimer?.Start(); WatchdogTimer.Start(); Stream = TcpClient.GetStream(); } }
private async Task WriteInternalAsync(byte[] bytes, CancellationToken cancellationToken) { try { await Stream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); InactivityTimer?.Reset(); } catch (Exception ex) { Disconnect($"Write error: {ex.Message}"); throw new ConnectionWriteException($"Failed to write {bytes.Length} bytes to {IPAddress}:{Port}: {ex.Message}", ex); } }
/// <summary> /// Disconnects the client. /// </summary> /// <param name="message">The optional message or reason for the disconnect.</param> public void Disconnect(string message = null) { if (State != ConnectionState.Disconnected && State != ConnectionState.Disconnecting) { ChangeState(ConnectionState.Disconnecting, message); InactivityTimer?.Stop(); WatchdogTimer?.Stop(); Stream?.Close(); TcpClient?.Close(); ChangeState(ConnectionState.Disconnected, message); } }
/// <summary> /// Releases the managed and unmanaged resources used by the <see cref="Connection"/>. /// </summary> /// <param name="disposing">A value indicating whether the object is in the process of disposing.</param> protected virtual void Dispose(bool disposing) { if (!Disposed) { if (disposing) { Disconnect("Connection is being disposed", new ObjectDisposedException(GetType().Name)); InactivityTimer?.Dispose(); WatchdogTimer?.Dispose(); Stream?.Dispose(); TcpClient?.Dispose(); } Disposed = true; } }
/// <summary> /// Releases the managed and unmanaged resources used by the <see cref="Connection"/>. /// </summary> /// <param name="disposing">A value indicating whether the object is in the process of disposing.</param> protected virtual void Dispose(bool disposing) { if (!Disposed) { if (disposing) { Disconnect(); InactivityTimer?.Dispose(); WatchdogTimer?.Dispose(); Stream?.Dispose(); TcpClient?.Dispose(); } Disposed = true; } }
private async Task ReadInternalAsync(long length, Stream outputStream, Func <CancellationToken, Task> governor, CancellationToken cancellationToken) { InactivityTimer?.Reset(); var buffer = new byte[TcpClient.Client.ReceiveBufferSize]; var totalBytesRead = 0; try { while (totalBytesRead < length) { await governor(cancellationToken).ConfigureAwait(false); var bytesRemaining = length - totalBytesRead; var bytesToRead = bytesRemaining > buffer.Length ? buffer.Length : (int)bytesRemaining; // cast to int is safe because of the check against buffer length. var bytesRead = await Stream.ReadAsync(buffer, 0, bytesToRead, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { throw new ConnectionException($"Remote connection closed"); } totalBytesRead += bytesRead; await outputStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); Interlocked.CompareExchange(ref DataRead, null, null)? .Invoke(this, new ConnectionDataEventArgs(totalBytesRead, length)); InactivityTimer?.Reset(); } await outputStream.FlushAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { Disconnect($"Read error: {ex.Message}", ex); if (ex is TimeoutException || ex is OperationCanceledException) { throw; } throw new ConnectionReadException($"Failed to read {length} bytes from {IPEndPoint}: {ex.Message}", ex); } }
/// <summary> /// Initializes a new instance of the <see cref="Connection"/> class. /// </summary> /// <param name="ipAddress">The remote IP address of the connection.</param> /// <param name="port">The remote port of the connection.</param> /// <param name="options">The optional options for the connection.</param> /// <param name="tcpClient">The optional TcpClient instance to use.</param> public Connection(IPAddress ipAddress, int port, ConnectionOptions options = null, ITcpClient tcpClient = null) { IPAddress = ipAddress; Port = port; Options = options ?? new ConnectionOptions(); TcpClient = tcpClient ?? new TcpClientAdapter(new TcpClient()); TcpClient.Client.ReceiveBufferSize = Options.ReadBufferSize; TcpClient.Client.SendBufferSize = Options.WriteBufferSize; if (Options.InactivityTimeout > 0) { InactivityTimer = new SystemTimer() { Enabled = false, AutoReset = false, Interval = Options.InactivityTimeout * 1000, }; InactivityTimer.Elapsed += (sender, e) => Disconnect($"Inactivity timeout of {Options.InactivityTimeout} seconds was reached."); } WatchdogTimer = new SystemTimer() { Enabled = false, AutoReset = true, Interval = 250, }; WatchdogTimer.Elapsed += (sender, e) => { if (!TcpClient.Connected) { Disconnect($"The server connection was closed unexpectedly."); } }; if (TcpClient.Connected) { State = ConnectionState.Connected; InactivityTimer?.Start(); WatchdogTimer.Start(); Stream = TcpClient.GetStream(); } }
private async Task WriteInternalAsync(long length, Stream inputStream, Func <CancellationToken, Task> governor, CancellationToken cancellationToken) { InactivityTimer?.Reset(); var sendBufferSize = TcpClient.Client.SendBufferSize; var inputBuffer = new byte[TcpClient.Client.SendBufferSize]; var totalBytesWritten = 0; try { while (totalBytesWritten < length) { await governor(cancellationToken).ConfigureAwait(false); var bytesRemaining = length - totalBytesWritten; var bytesToRead = bytesRemaining > sendBufferSize ? sendBufferSize : (int)bytesRemaining; var bytesRead = await inputStream.ReadAsync(inputBuffer, 0, bytesToRead, cancellationToken).ConfigureAwait(false); await Stream.WriteAsync(inputBuffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); totalBytesWritten += bytesRead; Interlocked.CompareExchange(ref DataWritten, null, null)? .Invoke(this, new ConnectionDataEventArgs(totalBytesWritten, length)); InactivityTimer?.Reset(); } } catch (Exception ex) { Disconnect($"Write error: {ex.Message}", ex); if (ex is TimeoutException || ex is OperationCanceledException) { throw; } throw new ConnectionWriteException($"Failed to write {length} bytes to {IPEndPoint}: {ex.Message}", ex); } }
private async Task <byte[]> ReadInternalAsync(int length, CancellationToken cancellationToken) { InactivityTimer?.Reset(); var result = new List <byte>(); var buffer = new byte[Options.BufferSize]; var totalBytesRead = 0; try { while (totalBytesRead < length) { var bytesRemaining = length - totalBytesRead; var bytesToRead = bytesRemaining > buffer.Length ? buffer.Length : bytesRemaining; var bytesRead = await Stream.ReadAsync(buffer, 0, bytesToRead, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { Disconnect($"Remote connection closed."); break; } totalBytesRead += bytesRead; var data = buffer.Take(bytesRead); result.AddRange(data); DataRead?.Invoke(this, new ConnectionDataEventArgs(data.ToArray(), totalBytesRead, length)); InactivityTimer?.Reset(); } return(result.ToArray()); } catch (Exception ex) { Disconnect($"Read error: {ex.Message}"); throw new ConnectionReadException($"Failed to read {length} bytes from {IPAddress}:{Port}: {ex.Message}", ex); } }
private async Task <byte[]> ReadInternalAsync(long length, CancellationToken cancellationToken) { InactivityTimer?.Reset(); var result = new List <byte>(); var buffer = new byte[TcpClient.Client.ReceiveBufferSize]; var totalBytesRead = 0; try { while (totalBytesRead < length) { var bytesRemaining = length - totalBytesRead; var bytesToRead = bytesRemaining > buffer.Length ? buffer.Length : (int)bytesRemaining; // cast to int is safe because of the check against buffer length. var bytesRead = await Stream.ReadAsync(buffer, 0, bytesToRead, cancellationToken).ConfigureAwait(false); if (bytesRead == 0) { throw new ConnectionException($"Remote connection closed"); } totalBytesRead += bytesRead; var data = buffer.Take(bytesRead); result.AddRange(data); DataRead?.Invoke(this, new ConnectionDataEventArgs(totalBytesRead, length)); InactivityTimer?.Reset(); } return(result.ToArray()); } catch (Exception ex) when(!(ex is TimeoutException) && !(ex is OperationCanceledException)) { Disconnect($"Read error: {ex.Message}"); throw new ConnectionReadException($"Failed to read {length} bytes from {IPAddress}:{Port}: {ex.Message}", ex); } }
public static void StopMiner(bool manually = false) { if (!process?.HasExited == true) { ManuallyStoped = true; } if (manually) { inactivity = false; InactivityTimer?.Invoke(-1); } else { WachdogInactivity(); } Waching = false; WachdogDelayTimer?.Invoke(-1); LowHashrate = false; LowHashrateTimer?.Invoke(-1); //Kill miner var processes = Process.GetProcesses(). Where(p => ProcessNames.Contains(p.ProcessName)); var res = Parallel.ForEach(processes, p => { while (!p.HasExited) { try { p.Kill(); } catch { } } }); while (!res.IsCompleted) { Thread.Sleep(50); } }
private void ResetInactivityTime() { InactivityTimer?.Reset(); LastActivityTime = DateTime.UtcNow; }
private static void StartWaching(double minhash) { WachdogThread?.Abort(); WachdogThread = new Thread(() => { WachdogInactivity(); Waching = true; double?[] hashes; IEnumerable <double?> activehashes; for (int i = Settings.Profile.TimeoutWachdog; i > -1; i--) { if (!Waching) { return; } Task.Run(() => { try { activehashes = GetMinerInfo().Hashrates.Where(h => h != null); if (activehashes.Sum() > minhash) { inactivity = false; InactivityTimer?.Invoke(-1); } } catch { } }); Task.Run(() => { WachdogDelayTimer?.Invoke(i); }); Thread.Sleep(1000); } Action WachdogLoop = (() => { hashes = GetMinerInfo().Hashrates; if (hashes != null) { activehashes = hashes.Where(h => h != null); //Zero if (activehashes.Sum() == 0) { ErrorsCounter++; if (ErrorsCounter > 4) { Task.Run(() => ZeroHash?.Invoke()); WachdogInactivity(); Waching = false; return; } } else { // низкий хешрейт if (activehashes.Sum() < minhash) { WachdogLowHashrate(); } else { // блок хорошего поведения LowHashrate = false; Inactivity = false; ErrorsCounter = 0; } //отвал карт if (hashes.Contains(0)) { ErrorsCounter++; if (ErrorsCounter > 4) { List <int> gpus = new List <int>(); for (int i = 0; i < hashes.Length; i++) { if (hashes[i] == 0) { gpus.Add(i); } } Task.Run(() => GPUsfalled?.Invoke(gpus.ToArray())); Waching = false; return; } } } return; } else { //бездаействие ErrorsCounter++; if (ErrorsCounter > 4) { Task.Run(() => ZeroHash?.Invoke()); WachdogInactivity(); Waching = false; return; } } WachdogInactivity(); }); while (Waching) { var WachResult = WachdogLoop.BeginInvoke(null, null); Thread.Sleep(1000); WachdogLoop.EndInvoke(WachResult); } }); WachdogThread.Start(); }
/// <summary> /// Asynchronously connects the client to the configured <see cref="IPEndPoint"/>. /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns>A Task representing the asynchronous operation.</returns> /// <exception cref="InvalidOperationException"> /// Thrown when the connection is already connected, or is transitioning between states. /// </exception> /// <exception cref="TimeoutException"> /// Thrown when the time attempting to connect exceeds the configured <see cref="ConnectionOptions.ConnectTimeout"/> value. /// </exception> /// <exception cref="OperationCanceledException"> /// Thrown when <paramref name="cancellationToken"/> cancellation is requested. /// </exception> /// <exception cref="ConnectionException">Thrown when an unexpected error occurs.</exception> public async Task ConnectAsync(CancellationToken?cancellationToken = null) { if (State != ConnectionState.Pending && State != ConnectionState.Disconnected) { throw new InvalidOperationException($"Invalid attempt to connect a connected or transitioning connection (current state: {State})"); } cancellationToken ??= CancellationToken.None; // create a new TCS to serve as the trigger which will throw when the CTS times out a TCS is basically a 'fake' task // that ends when the result is set programmatically. create another for cancellation via the externally provided token. var timeoutTaskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var cancellationTaskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); try { ChangeState(ConnectionState.Connecting, $"Connecting to {IPEndPoint}"); // create a new CTS with our desired timeout. when the timeout expires, the cancellation will fire using (var timeoutCancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(Options.ConnectTimeout))) { Task connectTask; if (Options.ProxyOptions != default) { var proxy = Options.ProxyOptions; connectTask = TcpClient.ConnectThroughProxyAsync( proxy.IPEndPoint.Address, proxy.IPEndPoint.Port, IPEndPoint.Address, IPEndPoint.Port, proxy.Username, proxy.Password, cancellationToken); } else { connectTask = TcpClient.ConnectAsync(IPEndPoint.Address, IPEndPoint.Port); } // register the TCS with the CTS. when the cancellation fires (due to timeout), it will set the value of the // TCS via the registered delegate, ending the 'fake' task, then bind the externally supplied CT with the same // TCS. either the timeout or the external token can now cancel the operation. #if NETSTANDARD2_0 using (timeoutCancellationTokenSource.Token.Register(() => timeoutTaskCompletionSource.TrySetResult(true))) using (((CancellationToken)cancellationToken).Register(() => cancellationTaskCompletionSource.TrySetResult(true))) #else await using (timeoutCancellationTokenSource.Token.Register(() => timeoutTaskCompletionSource.TrySetResult(true))) await using (((CancellationToken)cancellationToken).Register(() => cancellationTaskCompletionSource.TrySetResult(true))) #endif { var completedTask = await Task.WhenAny(connectTask, timeoutTaskCompletionSource.Task, cancellationTaskCompletionSource.Task).ConfigureAwait(false); if (completedTask == timeoutTaskCompletionSource.Task) { throw new TimeoutException($"Operation timed out after {Options.ConnectTimeout} milliseconds"); } else if (completedTask == cancellationTaskCompletionSource.Task) { throw new OperationCanceledException("Operation cancelled", cancellationToken.Value); } if (connectTask.Exception?.InnerException != null) { throw connectTask.Exception.InnerException; } } } InactivityTimer?.Start(); WatchdogTimer.Start(); Stream = TcpClient.GetStream(); ChangeState(ConnectionState.Connected, $"Connected to {IPEndPoint}"); } catch (Exception ex) { Disconnect($"Connection Error: {ex.Message}", ex); if (ex is TimeoutException || ex is OperationCanceledException) { throw; } throw new ConnectionException($"Failed to connect to {IPEndPoint}: {ex.Message}", ex); } }