/// <inheritdoc /> public byte[] SerialiseHeader(SendReceiveOptions options) { if (options == null) { throw new ArgumentNullException("options", "Provided SendReceiveOptions cannot be null."); } //We need to start of by serialising the header byte[] serialisedHeader; using (StreamTools.StreamSendWrapper sendWrapper = options.DataSerializer.SerialiseDataObject(_packetHeader, options.DataProcessors, null)) serialisedHeader = sendWrapper.ThreadSafeStream.ToArray(1); if (serialisedHeader.Length - 1 > byte.MaxValue) { throw new SerialisationException("Unable to send packet as header size is larger than Byte.MaxValue. Try reducing the length of provided packetTypeStr or turning off checkSum validation."); } //The first byte now specifies the header size (allows for variable header size) serialisedHeader[0] = (byte)(serialisedHeader.Length - 1); if (serialisedHeader == null) { throw new SerialisationException("Serialised header bytes should never be null."); } return(serialisedHeader); }
private void SendFile(Connection connection, string fileName) { try { FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); StreamTools.ThreadSafeStream safeStream = new StreamTools.ThreadSafeStream(stream); string shortFileName = System.IO.Path.GetFileName(fileName); long sendChunkSizeBytes = (long)(stream.Length / 20.0) + 1; long maxChunkSizeBytes = 500L * 1024L * 1024L; if (sendChunkSizeBytes > maxChunkSizeBytes) { sendChunkSizeBytes = maxChunkSizeBytes; } long totalBytesSent = 0; do { long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent); StreamTools.StreamSendWrapper streamWrapper = new StreamTools.StreamSendWrapper(safeStream, totalBytesSent, bytesToSend); long packetSequenceNumber; connection.SendObject("PartialFileData", streamWrapper, customOptions, out packetSequenceNumber); connection.SendObject("PartialFileDataInfo", new SendInfo(shortFileName, stream.Length, totalBytesSent, packetSequenceNumber), customOptions); totalBytesSent += bytesToSend; } while (totalBytesSent < stream.Length); GC.Collect(); } catch (CommunicationException) { } catch (Exception ex) { } }
/// <summary> /// Sends requested file to the remoteIP and port set in GUI /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SendFileButton_Click(object sender, RoutedEventArgs e) { //Create an OpenFileDialog so that we can request the file to send OpenFileDialog openDialog = new OpenFileDialog(); openDialog.Multiselect = false; //If a file was selected if (openDialog.ShowDialog() == true) { //Disable the send and compression buttons sendFileButton.IsEnabled = false; UseCompression.IsEnabled = false; //Parse the necessary remote information string filename = openDialog.FileName; string remoteIP = this.remoteIP.Text; string remotePort = this.remotePort.Text; //Set the send progress bar to 0 UpdateSendProgress(0); //Perform the send in a task so that we don't lock the GUI Task.Factory.StartNew(() => { try { //Create a fileStream from the selected file FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read); //Wrap the fileStream in a threadSafeStream so that future operations are thread safe StreamTools.ThreadSafeStream safeStream = new StreamTools.ThreadSafeStream(stream); //Get the filename without the associated path information string shortFileName = System.IO.Path.GetFileName(filename); //Parse the remote connectionInfo //We have this in a separate try catch so that we can write a clear message to the log window //if there are problems ConnectionInfo remoteInfo; try { remoteInfo = new ConnectionInfo(remoteIP, int.Parse(remotePort)); } catch (Exception) { throw new InvalidDataException("Failed to parse remote IP and port. Check and try again."); } //Get a connection to the remote side Connection connection = TCPConnection.GetConnection(remoteInfo); //Break the send into 20 segments. The less segments the less overhead //but we still want the progress bar to update in sensible steps long sendChunkSizeBytes = (long)(stream.Length / 20.0) + 1; //Limit send chunk size to 500MB long maxChunkSizeBytes = 500L * 1024L * 1024L; if (sendChunkSizeBytes > maxChunkSizeBytes) { sendChunkSizeBytes = maxChunkSizeBytes; } long totalBytesSent = 0; do { //Check the number of bytes to send as the last one may be smaller long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent); //Wrap the threadSafeStream in a StreamSendWrapper so that we can get NetworkComms.Net //to only send part of the stream. StreamTools.StreamSendWrapper streamWrapper = new StreamTools.StreamSendWrapper(safeStream, totalBytesSent, bytesToSend); //We want to record the packetSequenceNumber long packetSequenceNumber; //Send the select data connection.SendObject("PartialFileData", streamWrapper, customOptions, out packetSequenceNumber); //Send the associated SendInfo for this send so that the remote can correctly rebuild the data connection.SendObject("PartialFileDataInfo", new SendInfo(shortFileName, stream.Length, totalBytesSent, packetSequenceNumber), customOptions); totalBytesSent += bytesToSend; //Update the GUI with our send progress UpdateSendProgress((double)totalBytesSent / stream.Length); } while (totalBytesSent < stream.Length); //Clean up any unused memory GC.Collect(); AddLineToLog("Completed file send to '" + connection.ConnectionInfo.ToString() + "'."); } catch (CommunicationException) { //If there is a communication exception then we just write a connection //closed message to the log window AddLineToLog("Failed to complete send as connection was closed."); } catch (Exception ex) { //If we get any other exception which is not an InvalidDataException //we log the error if (!windowClosing && ex.GetType() != typeof(InvalidDataException)) { AddLineToLog(ex.Message.ToString()); LogTools.LogException(ex, "SendFileError"); } } //Once the send is finished reset the send progress bar UpdateSendProgress(0); //Once complete enable the send button again sendFileButton.Dispatcher.BeginInvoke(new Action(() => { sendFileButton.IsEnabled = true; UseCompression.IsEnabled = true; })); }); } }
/// <summary> /// Send the provided packet /// </summary> /// <param name="packet"></param> private void SendPacketSpecific(IPacket packet) { byte[] headerBytes; //Serialise the header if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled) { headerBytes = packet.SerialiseHeader(NetworkComms.InternalFixedSendReceiveOptions); } else { //If this connection does not use the application layer protocol we need to check a few things headerBytes = new byte[0]; if (packet.PacketHeader.PacketType != Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged)) { throw new UnexpectedPacketTypeException("Only 'Unmanaged' packet types can be used if the NetworkComms.Net application layer protocol is disabled."); } if (packet.PacketData.Length == 0) { throw new NotSupportedException("Sending a zero length array if the NetworkComms.Net application layer protocol is disabled is not supported."); } } double maxSendTimePerKB = double.MaxValue; if (!NetworkComms.DisableConnectionSendTimeouts) { if (SendTimesMSPerKBCache.Count > MinNumSendsBeforeConnectionSpecificSendTimeout) { maxSendTimePerKB = Math.Max(MinimumMSPerKBSendTimeout, SendTimesMSPerKBCache.CalculateMean() + NumberOfStDeviationsForWriteTimeout * SendTimesMSPerKBCache.CalculateStdDeviation()); } else { maxSendTimePerKB = DefaultMSPerKBSendTimeout; } } if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Debug("Sending a packet of type '" + packet.PacketHeader.PacketType + "' to " + ConnectionInfo + " containing " + headerBytes.Length.ToString() + " header bytes and " + packet.PacketData.Length.ToString() + " payload bytes. Allowing " + maxSendTimePerKB.ToString("0.0##") + " ms/KB for send."); } DateTime startTime = DateTime.Now; StreamTools.StreamSendWrapper[] streamsToSend = new StreamTools.StreamSendWrapper[] { new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(new MemoryStream(headerBytes), true)), packet.PacketData }; long totalBytesToSend = 0; foreach (StreamTools.StreamSendWrapper stream in streamsToSend) { totalBytesToSend += stream.Length; } //Send the streams double[] timings = SendStreams(streamsToSend, maxSendTimePerKB, totalBytesToSend); //Record the timings double timingsSum = 0; for (int i = 0; i < timings.Length; i++) { timingsSum += timings[i]; SendTimesMSPerKBCache.AddValue(timings[i], streamsToSend[i].Length); } SendTimesMSPerKBCache.TrimList(MaxNumSendTimes); if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace(" ... " + (totalBytesToSend / 1024.0).ToString("0.000") + "KB sent at average of " + ((totalBytesToSend / 1024.0) / (DateTime.Now - startTime).TotalSeconds).ToString("0.000") + "KB/s. Current:" + (timingsSum / timings.Length).ToString("0.00") + " ms/KB, Connection Avg:" + SendTimesMSPerKBCache.CalculateMean().ToString("0.00") + " ms/KB."); } }
/// <summary> /// Implementation for sending a null packets on this connection type. Used for ensuring a connection /// is not terminated by an intermediary switch/router due to inactivity. /// </summary> private void SendNullPacket() { //We don't send null packets for UDP if (ConnectionInfo.ConnectionType == ConnectionType.UDP) { return; } //We can't send null packets if the application layer is disabled //as we have no way to distinguish them on the receiving side if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Ignoring null packet send to " + ConnectionInfo + " as the application layer protocol is disabled."); } return; } try { //Only once the connection has been established do we send null packets if (ConnectionInfo.ConnectionState == ConnectionState.Established) { //Multiple threads may try to send packets at the same time so we need this lock to prevent a thread cross talk lock (sendLocker) { if (NetworkComms.LoggingEnabled) { NetworkComms.Logger.Trace("Sending null packet to " + ConnectionInfo); } //Send a single 0 byte double maxSendTimePerKB = double.MaxValue; if (!NetworkComms.DisableConnectionSendTimeouts) { if (SendTimesMSPerKBCache.Count > MinNumSendsBeforeConnectionSpecificSendTimeout) { maxSendTimePerKB = Math.Max(MinimumMSPerKBSendTimeout, SendTimesMSPerKBCache.CalculateMean() + NumberOfStDeviationsForWriteTimeout * SendTimesMSPerKBCache.CalculateStdDeviation()); } else { maxSendTimePerKB = DefaultMSPerKBSendTimeout; } } StreamTools.StreamSendWrapper[] streamsToSend = new StreamTools.StreamSendWrapper[] { new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(new MemoryStream(new byte[] { 0 }), true)) }; SendStreams(streamsToSend, maxSendTimePerKB, 1); //Update the traffic time after we have written to netStream ConnectionInfo.UpdateLastTrafficTime(); } } //If the connection is shutdown we should call close if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown) { CloseConnection(false, -8); } } catch (Exception) { CloseConnection(true, 19); } }
/* * Transfer code partially retrieved from NetworkComms example https://www.networkcomms.net/creating-a-wpf-file-transfer-application/ * on 07.10.2020 */ private void btnUpload_Click(object sender, EventArgs e) { string filename = openFileDialog1.FileName; if (File.Exists(filename)) { btnUpload.Enabled = false; btnBrowse.Enabled = false; this.isBusy = true; progressBar1.Value = (int)0; Task.Factory.StartNew(() => { try { //Create a fileStream from the selected file FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read); //Wrap the fileStream in a threadSafeStream so that future operations are thread safe StreamTools.ThreadSafeStream safeStream = new StreamTools.ThreadSafeStream(stream); //Get the filename without the associated path information string shortFileName = System.IO.Path.GetFileName(filename); //Parse the remote connectionInfo //We have this in a separate try catch so that we can write a clear message to the log window //if there are problems //Get a connection to the remote side Connection connection = AdminNetwork.Connection(); //Break the send into 20 segments. The less segments the less overhead //but we still want the progress bar to update in sensible steps long sendChunkSizeBytes = (long)(stream.Length / 20.0) + 1; //Limit send chunk size to 500MB long maxChunkSizeBytes = 500L * 1024L * 1024L; if (sendChunkSizeBytes > maxChunkSizeBytes) { sendChunkSizeBytes = maxChunkSizeBytes; } long totalBytesSent = 0; do { //Check the number of bytes to send as the last one may be smaller long bytesToSend = (totalBytesSent + sendChunkSizeBytes < stream.Length ? sendChunkSizeBytes : stream.Length - totalBytesSent); //Wrap the threadSafeStream in a StreamSendWrapper so that we can get NetworkComms.Net //to only send part of the stream. StreamTools.StreamSendWrapper streamWrapper = new StreamTools.StreamSendWrapper(safeStream, totalBytesSent, bytesToSend); //We want to record the packetSequenceNumber long packetSequenceNumber; //Send the select data connection.SendObject("PartialFileData", streamWrapper, NetworkComms.DefaultSendReceiveOptions, out packetSequenceNumber); //Send the associated SendInfo for this send so that the remote can correctly rebuild the data connection.SendObject("PartialFileDataInfo", new SendInfo(shortFileName, stream.Length, totalBytesSent, packetSequenceNumber), NetworkComms.DefaultSendReceiveOptions); totalBytesSent += bytesToSend; //Update the GUI with our send progress UpdateSendProgress(shortFileName, (double)totalBytesSent / stream.Length); } while (totalBytesSent < stream.Length); //Clean up any unused memory GC.Collect(); UpdateSendProgress(shortFileName, 100); // AddLineToLog("Completed file send to '" + connection.ConnectionInfo.ToString() + "'."); } catch (CommunicationException) { //If there is a communication exception then we just write a connection //closed message to the log window // AddLineToLog("Failed to complete send as connection was closed."); MessageBox.Show("Failed to complete send as connection was closed."); } catch (Exception ex) { //If we get any other exception which is not an InvalidDataException //we log the error /* if (!windowClosing && ex.GetType() != typeof(InvalidDataException)) * { * AddLineToLog(ex.Message.ToString()); * LogTools.LogException(ex, "SendFileError"); * }*/ MessageBox.Show(ex.ToString()); } }); } }
private void Constructor <payloadObjectType>(string sendingPacketTypeStr, string requestReturnPacketTypeStr, payloadObjectType payloadObject, SendReceiveOptions options, bool isNested) { if (sendingPacketTypeStr == null || sendingPacketTypeStr == "") { throw new ArgumentNullException("sendingPacketTypeStr", "The provided string can not be null or zero length."); } if (options == null) { throw new ArgumentNullException("options", "The provided SendReceiveOptions cannot be null."); } if (options.DataSerializer == null) { throw new ArgumentNullException("options", "The provided SendReceiveOptions.DataSerializer cannot be null. Consider using NullSerializer instead."); } //Check for security critical data processors //There may be performance issues here bool containsSecurityCritialDataProcessors = false; if (!options.Options.ContainsKey("UseNestedPacketType") && //We only need to perform this check if we are not already using a nested packet !isNested) //We do not perform this check within a nested packet { foreach (DataProcessor processor in options.DataProcessors) { if (processor.IsSecurityCritical) { containsSecurityCritialDataProcessors = true; break; } } } //By default the object to serialise will be the payloadObject object objectToSerialise = payloadObject; bool objectToSerialiseIsNull = false; //We only replace the null with an empty stream if this is either in the nested packet //or we will not be nesting if (objectToSerialise == null && ((!options.Options.ContainsKey("UseNestedPacketType") && !containsSecurityCritialDataProcessors) || isNested)) { #if NETFX_CORE var emptyStream = new MemoryStream(new byte[0], 0, 0, false); #else var emptyStream = new MemoryStream(new byte[0], 0, 0, false, true); #endif //If the sending object is null we set objectToSerialiseIsNull and create a zero length StreamSendWrapper //The zero length StreamSendWrapper can then be passed to any data serializers objectToSerialiseIsNull = true; objectToSerialise = new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(emptyStream, true)); } //If we need to nest this packet if ((containsSecurityCritialDataProcessors || options.Options.ContainsKey("UseNestedPacketType")) && !isNested) { //We set the objectToSerialise to a nested packet objectToSerialise = new Packet(sendingPacketTypeStr, requestReturnPacketTypeStr, payloadObject, options, true); } else if (isNested) { //Serialise the payload object into byte[] //We do not use any data processors at this stage as the object will be processed again one level higher. #if NETFX_CORE _payloadObjectBytes = options.DataSerializer.SerialiseDataObject(payloadObject).ThreadSafeStream.ToArray(); _payloadSize = _payloadObjectBytes.Length; #else NetworkCommsDotNet.Tools.StreamTools.ThreadSafeStream tempStream = options.DataSerializer.SerialiseDataObject(objectToSerialise).ThreadSafeStream; _payloadObjectBytes = tempStream.GetBuffer(); _payloadSize = (int)tempStream.Length; #endif //Set the packet header //THe nulls represent internal SendReceiveOptions and no checksum this._packetHeader = new PacketHeader(sendingPacketTypeStr, _payloadSize, null, requestReturnPacketTypeStr, null); //Set the deserialiser information in the nested packet header, excluding data processors this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(options.DataSerializer, null)); } //If we are at the top level packet we can finish off the serialisation if (!isNested) { //Set the payload stream data. if (objectToSerialiseIsNull && options.DataProcessors.Count == 0) { //Only if there are no data processors can we use a zero length array for nulls //This ensures that should there be any required padding we can include it this.payloadStream = (StreamTools.StreamSendWrapper)objectToSerialise; } else { if (objectToSerialise is Packet) { //We have to use the internal explicit serializer for nested packets (the nested data is already byte[]) this.payloadStream = NetworkComms.InternalFixedSendReceiveOptions.DataSerializer.SerialiseDataObject(objectToSerialise, options.DataProcessors, options.Options); } else { this.payloadStream = options.DataSerializer.SerialiseDataObject(objectToSerialise, options.DataProcessors, options.Options); } } //We only calculate the checkSum if we are going to use it string hashStr = null; if (NetworkComms.EnablePacketCheckSumValidation) { hashStr = StreamTools.MD5(payloadStream.ThreadSafeStream.ToArray(payloadStream.Start, payloadStream.Length)); } //Choose the sending and receiving packet type depending on if it is being used with a nested packet string _sendingPacketTypeStr; string _requestReturnPacketTypeStr = null; if (containsSecurityCritialDataProcessors || options.Options.ContainsKey("UseNestedPacketType")) { _sendingPacketTypeStr = Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.NestedPacket); } else { _sendingPacketTypeStr = sendingPacketTypeStr; _requestReturnPacketTypeStr = requestReturnPacketTypeStr; } this._packetHeader = new PacketHeader(_sendingPacketTypeStr, payloadStream.Length, options, _requestReturnPacketTypeStr, hashStr); //Add an identifier specifying the serialisers and processors we have used if (objectToSerialise is Packet) { this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(NetworkComms.InternalFixedSendReceiveOptions.DataSerializer, options.DataProcessors)); } else { this._packetHeader.SetOption(PacketHeaderLongItems.SerializerProcessors, DPSManager.CreateSerializerDataProcessorIdentifier(options.DataSerializer, options.DataProcessors)); } } //Set the null data header section if required if (objectToSerialiseIsNull && ((!containsSecurityCritialDataProcessors && !options.Options.ContainsKey("UseNestedPacketType")) || isNested)) { this._packetHeader.SetOption(PacketHeaderStringItems.NullDataSection, ""); } if (NetworkComms.LoggingEnabled) { if (isNested) { NetworkComms.Logger.Trace(" ... created nested packet of type " + sendingPacketTypeStr); } else { NetworkComms.Logger.Trace(" ... created packet of type " + sendingPacketTypeStr + ". PacketObject data size is " + payloadStream.Length.ToString() + " bytes"); } } }