/// <summary> /// Cancel the task /// </summary> public override void Cancel() { if (AbortTransfer != null) { AbortTransfer.Cancel(); } StopReceiving = true; }
/// <summary> /// Reject the transfer /// </summary> /// <param name="RejectAll">If I have to stop the thread</param> public void Reject(bool RejectAll = false) { try { byte[] response = Encoding.UTF8.GetBytes("KO"); Stream.Write(response, 0, response.Length); } catch (Exception) { AbortTransfer.Cancel(); /*if (RejectAll) */ StopReceiving = true; return; } }
/// <summary> /// Cancel current transfer /// </summary> public override void Cancel() { lock (Buffer) { // Have to wake up who might be sleeping Monitor.PulseAll(Buffer); // I put Cancel here, so that if the loader or sender are waiting for the lock, // I am sure that they will get the cancellation when they get the lock // NOTE: I may have been called *BEFORE* even starting the sender and the loader, // that's why I am checking for null. No worries for Monitor.PulseAll, because it won't wake anyone. if (AbortTransfer != null) { AbortTransfer.Cancel(); } } }
/// <summary> /// Send the file /// </summary> private void Transmit() { //------------------------------------ // Init //------------------------------------ long written = 0; //------------------------------------ // Get the file data //------------------------------------ Progress.CurrentActivity = "Transferring file..."; while (written < FileSize && !AbortTransfer.IsCancellationRequested) { // How much should I write int get = (int)(FileSize - written < 4096 ? (FileSize - written) : 4096); byte[] data = new byte[get]; // Try to acquire the buffer. I can't write if nothing's there to write yet, man. lock (Buffer) { if (Buffer.Count == 0) { /** * Read considerations I wrote for Monitor.Wait in Loader's code **/ if (!AbortTransfer.Token.IsCancellationRequested) { Monitor.Wait(Buffer); } // Did you wake me up because there's data or because we have to exit? // Doing break, so it gracefully exits the method if (AbortTransfer.Token.IsCancellationRequested) { break; } } data = Buffer.Dequeue(); // Wake up the sender if it was waiting for a dequeue Monitor.Pulse(Buffer); } //------------------------------------ // Send data //------------------------------------ try { // Send data Task w = Stream.WriteAsync(data, 0, data.Length); // I really need the result now. Don't want to send packets out of order. w.Wait(AbortTransfer.Token); w.Dispose(); // Update written count written += get; Progress.Completion = written; } catch (Exception e) { lock (Buffer) { // I have to notify the Loader that I couldn't write! // Read the considerations in the Loader. Monitor.Pulse(Buffer); // Cancel the loader. AbortTransfer.Cancel(); if (e is SocketException) { Progress.CurrentActivity = "Socket has been closed"; } else { if (e is OperationCanceledException) { Progress.CurrentActivity = "Operation cancelled"; } else { Progress.CurrentActivity = "The other peer cancelled the operation"; } } Progress.IsError = true; StopSending = true; } } } // Send Digest if (written == FileSize && !AbortTransfer.Token.IsCancellationRequested) { lock (this) { if (AbortTransfer.Token.IsCancellationRequested) { return; } if (Buffer.Count == 0) { Monitor.Wait(this); } if (AbortTransfer.Token.IsCancellationRequested) { return; } byte[] Digest = Buffer.Dequeue(); using (Task t = Stream.WriteAsync(Digest, 0, 32)) { t.Wait(AbortTransfer.Token); } } } }
/// <summary> /// Load bytes from the disk /// </summary> private void Load() { //------------------------------------ // Init //------------------------------------ long read = 0; // https://stackoverflow.com/questions/34325034/how-do-i-cancel-a-filestream-readasync-request/34325233#34325233 // If no IsAsync is specified, readasync does not really implement cancellation. // NOTE: I started the project with the name FileShare, without knowing it was included in System.IO... using (FileStream file = new FileStream(FilePath, FileMode.Open, FileAccess.Read, System.IO.FileShare.Read, 4096)) { //------------------------------------ // Load data //------------------------------------ while (read < FileSize && !AbortTransfer.Token.IsCancellationRequested) { // How many bytes you should read // Casted to int as the maximum will always be 4096, which is the defaults size of Read btw int get = (int)(FileSize - read < 4096 ? (FileSize - read) : 4096); byte[] _data = new byte[get]; byte[] data; int _read = 0; try { // Actually load data // It seems that the overload ReadAsync(byte[], int32, int32, CancellationToken) // doesn't really support cancellation. So I made it with a Wait(CancellationToken) using (Task <int> r = file.ReadAsync(_data, 0, get)) { r.Wait(AbortTransfer.Token); // Completed? _read = r.Result; // As per documentation, ReadAsync reads 0 bytes if EOF is reached, // I'm checking it anyway... if (_read == 0) { throw new EndOfStreamException(); } data = new byte[_read]; Array.Copy(_data, data, _read); } } catch (Exception) { // Notify the Sender that we must exit lock (Buffer) { // If you're here, it means that you were able to get the lock either // because the Sender is sending or because it's sleeping. // In the latter case I *MUST* tell it to wake up and exit Monitor.Pulse(Buffer); // Set the cancellation token. // I *MUST* set the cancellation token *WHILE* still in lock, // so that i can cancel the operation while the sender is still blocked, // ensuring that it will catch the cancellation. AbortTransfer.Cancel(); } return; } //------------------------------------ // Store data on buffer //------------------------------------ lock (Buffer) { // I only allow a maximum number of 5 elements, // so the >= is useless here. if (Buffer.Count >= 5) { /** * Note: I have to wrap Monitor.Wait in two if(Token.CancellationRequested) * because I have to check if task has been cancelled!. * The first one is because user might have interrupted it OR the sender had an exception. * The second one is to check if I have been awoken because there is data OR I have to quit. **/ if (!AbortTransfer.Token.IsCancellationRequested) { Monitor.Wait(Buffer); } // Did you wake me up because there's data or because we have to exit? // Doing break, so it gracefully exits the loop if (AbortTransfer.Token.IsCancellationRequested) { break; } } // Put data into buffer Buffer.Enqueue(data); // Wake up the Sender if it was sleeping (which is probably so the first time) Monitor.Pulse(Buffer); } // Update how many data I have sent read += _read; } //------------------------------------ // Send Digest //------------------------------------ if (read == FileSize && !AbortTransfer.Token.IsCancellationRequested) { ComputeDigest(file); } } }