private void SendWorker() { byte[] block = new byte[512]; while (true) { // // Send the file 512 bytes at a time to the channel, and then finish. // int read = _data.Read(block, 0, block.Length); _channel.Send(block, read, true); Log.Write(LogType.Verbose, LogComponent.EFTP, "Sent data, position is now {0}", _data.Position); if (read != block.Length) { _channel.SendEnd(); break; } } _data.Close(); _sendDone = true; EFTPManager.DestroyChannel(_channel); }
/// <summary> /// Sends data to the channel (i.e. to the client). Will block (waiting for an ACK) if an ACK is requested. /// </summary> /// <param name="data">The data to be sent</param> /// <param name="flush">Whether to flush data out immediately or to wait for enough for a full PUP first.</param> public void Send(byte[] data, int length, bool flush) { if (length > data.Length) { throw new InvalidOperationException("Length must be less than or equal to the size of data."); } // Add output data to output queue. // Again, this is really inefficient. for (int i = 0; i < length; i++) { _outputQueue.Enqueue(data[i]); } if (flush || _outputQueue.Count >= PUP.MAX_PUP_SIZE) { // Send data until all is used (for a flush) or until we have less than a full PUP (non-flush). while (_outputQueue.Count >= (flush ? 1 : PUP.MAX_PUP_SIZE)) { byte[] chunk = new byte[Math.Min(PUP.MAX_PUP_SIZE, _outputQueue.Count)]; // Ugh. for (int i = 0; i < chunk.Length; i++) { chunk[i] = _outputQueue.Dequeue(); } while (true) { // Send the data. PUP dataPup = new PUP(PupType.EFTPData, _sendPos, _clientConnectionPort, _serverConnectionPort, chunk); Router.Instance.SendPup(dataPup); // Await an ACK. We will retry several times and resend as necessary. int retry = 0; for (retry = 0; retry < EFTPRetryCount; retry++) { if (_outputAckEvent.WaitOne(EFTPAckTimeoutPeriod)) { // done, we got our ACK. break; } else { // timeout: resend the PUP and wait for an ACK again. Router.Instance.SendPup(dataPup); } } if (retry >= EFTPRetryCount) { Log.Write(LogType.Error, LogComponent.EFTP, "Timeout waiting for ACK, aborting connection."); SendAbort("Client unresponsive."); EFTPManager.DestroyChannel(this); } if (_lastRecvPos == _sendPos) { // The client is in sync with us, we are done with this packet. break; } else if (_sendPos - _lastRecvPos > 1) { // We lost more than one packet, something is very broken. Log.Write(LogType.Error, LogComponent.EFTP, "Client lost more than one packet, connection is broken. Aborting."); SendAbort("Client lost too much data."); EFTPManager.DestroyChannel(this); } else { // We lost one packet, move back and send it again. Log.Write(LogType.Warning, LogComponent.EFTP, "Client lost a packet, resending."); } } // Move to next packet. _sendPos++; } } }