/// <summary> /// Begins to send a file through the RendezvousData's Transfer socket /// </summary> /// <remarks>This method cancels the direct connection itself rather than throw an exception</remarks> private void BeginSendFile(RendezvousData rd) { // Open the file for reading try { rd.DirectConnection.DataChunk = new byte[8192]; rd.DirectConnection.DataStream = (new StreamReader(rd.DirectConnection.LocalFileName, false)).BaseStream; rd.DirectConnection.StreamPosition = 0; } catch (Exception ex) { rd.DirectConnection.CancelTransfer("Can't open file for reading"); return; } // Send a PROMPT message and receive an ACK message try { SendFileTransmitterHandshake(rd); } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Signal the parent session that we've started transfering a file rd.ParentSession.OnFileTransferProgress( RendezvousManager.GetKeyFromCookie(rd.Cookie), 0, rd.DirectConnection.Header.Size); // Send the first chunk SendFileTransmitterSendChunk(rd); }
/// <summary> /// Called when a chunk of data has been sent to the remote client /// </summary> /// <param name="res">An <see cref="IAsyncResult"/> object</param> private void SendFileTransmitterSendEnd(IAsyncResult res) { RendezvousData rd = (RendezvousData)res.AsyncState; int bytessent = 0; try { bytessent = rd.DirectConnection.Transfer.EndSend(res); } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } rd.DirectConnection.StreamPosition += (uint)bytessent; // Signal parent session of the progress rd.ParentSession.OnFileTransferProgress( RendezvousManager.GetKeyFromCookie(rd.Cookie), rd.DirectConnection.StreamPosition, rd.DirectConnection.Header.Size); // If all the file has been sent, wait for the acknowledgement message // and close up shop. Otherwise, send another chunk if (rd.DirectConnection.StreamPosition == rd.DirectConnection.Header.Size) { byte[] receivedone = new byte[256]; try { rd.DirectConnection.Transfer.Receive(receivedone); } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Check checksums? rd.DirectConnection.CompleteTransfer(); } else { SendFileTransmitterSendChunk(rd); } }
/// <summary> /// The callback function for socket reads during a file transfer /// </summary> /// <param name="res">An <see cref="IAsyncResult"/> object</param> private void SendFileTransferReceive(IAsyncResult res) { int bytesread = 0; RendezvousData rd = (RendezvousData)res.AsyncState; DirectConnectInfo fti = rd.DirectConnection; // End the async receive operation try { bytesread = fti.Transfer.EndReceive(res); if (bytesread == 0) { throw new Exception("Remote client cancelled transfer"); } } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Write the received data out to the file try { fti.DataStream.Write(fti.DataChunk, 0, bytesread); fti.StreamPosition += (uint)bytesread; } catch (Exception ex) { rd.DirectConnection.CancelTransfer(ex.Message); return; } // Checksum the received chunk fti.Header.ReceivedChecksum = ChecksumChunk(fti.DataChunk, (uint)bytesread, fti.Header.ReceivedChecksum); // Signal progress to the parent session rd.ParentSession.OnFileTransferProgress( RendezvousManager.GetKeyFromCookie(rd.Cookie), rd.DirectConnection.StreamPosition, rd.DirectConnection.Header.Size); // Check to see if the transfer has finished if (fti.StreamPosition >= fti.Header.Size) { fti.DataChunk = null; fti.DataStream.Close(); // Send out the acknowledgement, compare the checksums, and finish up SendFileReceiverDone(rd); if (fti.Header.ReceivedChecksum != fti.Header.Checksum) { rd.DirectConnection.CancelTransfer("Received data does not match expected checksum"); return; } rd.DirectConnection.CompleteTransfer(); } else { // Keep receiving asynchronously fti.Transfer.BeginReceive(fti.DataChunk, 0, fti.DataChunk.Length, SocketFlags.None, new AsyncCallback(SendFileTransferReceive), rd); } }
/// <summary> /// Receives the PROMPT message and responds with an ACK message, then prepares the transfer socket to receive data /// </summary> /// <param name="rd">The <see cref="RendezvousData"/> object receiving a file</param> private void BeginReceiveFile(RendezvousData rd) { byte[] filetransferheader = null; int index = 0; // Read in 256 bytes, PROMPT type and blank cookie filetransferheader = new byte[256]; try { while (index < filetransferheader.Length) { index += rd.DirectConnection.Transfer.Receive( filetransferheader, index, filetransferheader.Length - index, SocketFlags.None); } } catch (Exception ex) { string message = "Error negotiating file transfer:" + Environ.NewLine + ex.Message; throw new Exception(message); } index = 8; DirectConnectInfo dci = rd.DirectConnection; //Marshal.ByteArrayToFTI(filetransferheader, ref index, ref dci); rd.DirectConnection = dci; // Just to be sure // Respond with the same header, but with the ACK type and the ICBM cookie set index = 6; Marshal.InsertUshort(filetransferheader, 0x0202, ref index); Marshal.CopyArray(rd.Cookie, filetransferheader, 0, ref index); index = 0; try { while (index < filetransferheader.Length) { index += rd.DirectConnection.Transfer.Send( filetransferheader, index, filetransferheader.Length - index, SocketFlags.None); } } catch (Exception ex) { string message = "Error negotiating file transfer:" + Environ.NewLine + ex.Message; throw new Exception(message); } // Open the file for writing try { rd.DirectConnection.DataChunk = new byte[8192]; rd.DirectConnection.DataStream = (new StreamWriter(rd.DirectConnection.LocalFileName, false)).BaseStream; rd.DirectConnection.StreamPosition = 0; } catch (Exception) { throw new Exception("Can't open target file for writing"); } // Signal the parent session that the file transfer has started rd.ParentSession.OnFileTransferProgress(RendezvousManager.GetKeyFromCookie(rd.Cookie), 0, rd.DirectConnection.Header.Size); // Start receiving data rd.DirectConnection.Transfer.BeginReceive(rd.DirectConnection.DataChunk, 0, rd.DirectConnection.DataChunk.Length, SocketFlags.None, new AsyncCallback(SendFileTransferReceive), rd); }