示例#1
0
        /// <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);
            }
        }
示例#6
0
        /*
         *  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());
                    }
                });
            }
        }
示例#7
0
        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");
                }
            }
        }