/// <summary>
        /// Initialise the item data from an assembly config
        /// </summary>
        /// <param name="assemblyConfig"></param>
        public DistributedItemData(ItemAssemblyConfig assemblyConfig)
        {
            this.DataBuildMode        = assemblyConfig.ItemBuildMode;
            this.TotalNumChunks       = assemblyConfig.TotalNumChunks;
            this.ChunkSizeInBytes     = assemblyConfig.ChunkSizeInBytes;
            this.CompleteDataCheckSum = assemblyConfig.CompleteDataCheckSum;
            this.ChunkCheckSums       = assemblyConfig.ChunkCheckSums;
            this.ItemBytesLength      = assemblyConfig.TotalItemSizeInBytes;

            InitialiseChunkPositionLengthDict();

            #region Build Internal Data Structure
            //Create the data stores as per the assembly config
            if (DataBuildMode == DataBuildMode.Memory_Single)
            //ItemBuildMode == ItemBuildMode.Both_Single ||)
            {
                MemoryStream itemStream = new MemoryStream(0);
                itemStream.SetLength(ItemBytesLength);
                CompleteDataStream = new StreamTools.ThreadSafeStream(itemStream);
            }
            else if (DataBuildMode == DataBuildMode.Disk_Single)
            {
                #region DiskSingle
                string     folderLocation = "DFS_" + NetworkComms.NetworkIdentifier;
                string     fileName       = Path.Combine(folderLocation, assemblyConfig.ItemIdentifier + ".DFSItemData");
                FileStream fileStream;
                if (File.Exists(fileName))
                {
                    //If the file already exists the MD5 had better match otherwise we have a problem
                    try
                    {
                        fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                        if (StreamTools.MD5(fileStream) != assemblyConfig.CompleteDataCheckSum)
                        {
                            throw new Exception("Wrong place, wrong time, wrong file!");
                        }
                    }
                    catch (Exception)
                    {
                        try
                        {
                            File.Delete(fileName);
                            fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
                        }
                        catch (Exception)
                        {
                            throw new Exception("File with name '" + fileName + "' already exists. Unfortunately the MD5 does match the expected DFS item. Unable to delete in order to continue.");
                        }
                    }
                }
                else
                {
                    lock (DFS.globalDFSLocker)
                    {
                        if (!Directory.Exists(folderLocation))
                        {
                            Directory.CreateDirectory(folderLocation);
                        }
                    }

                    fileStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose);
                    fileStream.SetLength(ItemBytesLength);
                    fileStream.Flush();
                }

                CompleteDataStream = new StreamTools.ThreadSafeStream(fileStream);

                if (!File.Exists(fileName))
                {
                    throw new Exception("At this point the item data file should have been created. This exception should not really be possible.");
                }
                #endregion
            }
            else
            {
                #region Data Chunks
                //Break the itemDataStream into blocks
                ChunkDataStreams = new StreamTools.ThreadSafeStream[ChunkPositionLengthDict.Count];

                for (int i = 0; i < ChunkPositionLengthDict.Count; i++)
                {
                    //We now need to available the respective data into blocks
                    Stream destinationStream = null;
                    if (DataBuildMode == DataBuildMode.Disk_Blocks)
                    {
                        string folderLocation = "DFS_" + NetworkComms.NetworkIdentifier;
                        string fileName       = Path.Combine(folderLocation, assemblyConfig.ItemIdentifier + ".DFSItemData_" + i.ToString());

                        if (File.Exists(fileName))
                        {
                            try
                            {
                                destinationStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                                if (assemblyConfig.ChunkCheckSums != null && StreamTools.MD5(destinationStream) != assemblyConfig.ChunkCheckSums[i])
                                {
                                    throw new Exception("Wrong place, wrong time, wrong file!");
                                }
                            }
                            catch (Exception)
                            {
                                try
                                {
                                    File.Delete(fileName);
                                    destinationStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8192, FileOptions.DeleteOnClose);
                                }
                                catch (Exception)
                                {
                                    throw new Exception("File with name '" + fileName + "' already exists. Unfortunately the MD5 does match the expected DFS item. Unable to delete in order to continue.");
                                }
                            }
                        }
                        else
                        {
                            //Create the folder if it does not exist yet
                            lock (DFS.globalDFSLocker)
                            {
                                if (!Directory.Exists(folderLocation))
                                {
                                    Directory.CreateDirectory(folderLocation);
                                }
                            }

                            destinationStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8192, FileOptions.DeleteOnClose);
                        }

                        if (!File.Exists(fileName))
                        {
                            throw new Exception("At this point the item data file should have been created. This exception should not really be possible.");
                        }
                    }
                    else
                    {
                        //If we are not exclusively building to the disk we just use a memory stream at this stage
                        destinationStream = new MemoryStream(ChunkPositionLengthDict[i].Length);
                    }

                    //Ensure we start at the beginning of the stream
                    destinationStream.SetLength(ChunkPositionLengthDict[i].Length);
                    destinationStream.Flush();
                    destinationStream.Seek(0, SeekOrigin.Begin);
                    ChunkDataStreams[i] = new StreamTools.ThreadSafeStream(destinationStream);
                }
                #endregion
            }
            #endregion
        }
        /// <summary>
        /// Sets the item data using the provided data stream. Useful for setting data after deserialisation
        /// </summary>
        /// <param name="itemIdentifier"></param>
        /// <param name="itemDataStream"></param>
        public void SetData(string itemIdentifier, Stream itemDataStream)
        {
            lock (dataLocker)
            {
                if (itemIdentifier == null)
                {
                    throw new ArgumentNullException("itemIdentifier");
                }
                if (itemDataStream == null)
                {
                    throw new ArgumentNullException("itemDataStream");
                }

                #region Build Internal Data Structure
                if (DataBuildMode == DataBuildMode.Disk_Single ||
                    //ItemBuildMode == ItemBuildMode.Both_Single ||
                    DataBuildMode == DataBuildMode.Memory_Single)
                {
                    CompleteDataStream = new StreamTools.ThreadSafeStream(itemDataStream);
                }
                else
                {
                    //Break the itemDataStream into blocks
                    ChunkDataStreams = new StreamTools.ThreadSafeStream[ChunkPositionLengthDict.Count];

                    //If the itemDataStream is a memory stream we can try to access the buffer as it makes creating the streams more efficient
                    byte[] itemDataStreamBuffer = null;
                    if (itemDataStream is MemoryStream)
                    {
                        try
                        {
                            itemDataStreamBuffer = ((MemoryStream)itemDataStream).GetBuffer();
                        }
                        catch (UnauthorizedAccessException) { /* Ignore */ }
                    }

                    for (int i = 0; i < ChunkPositionLengthDict.Count; i++)
                    {
                        if (itemDataStreamBuffer != null)
                        {
                            //This is the fastest way to create a block of data streams
                            ChunkDataStreams[i] = new StreamTools.ThreadSafeStream(new MemoryStream(itemDataStreamBuffer, ChunkPositionLengthDict[i].Position, ChunkPositionLengthDict[i].Length));
                        }
                        else
                        {
                            //We now need to available the respective data into blocks
                            Stream destinationStream = null;
                            if (DataBuildMode == DataBuildMode.Disk_Blocks)
                            {
                                string folderLocation = "DFS_" + NetworkComms.NetworkIdentifier;
                                string fileName       = Path.Combine(folderLocation, itemIdentifier + ".DFSItemData_" + i.ToString());

                                if (File.Exists(fileName))
                                {
                                    destinationStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                                    //if (StreamTools.MD5(destinationStream) != checksum)
                                    //    throw new Exception("Wrong place, wrong time, wrong file!");
                                }
                                else
                                {
                                    //Create the folder if it does not exist yet
                                    lock (DFS.globalDFSLocker)
                                    {
                                        if (!Directory.Exists(folderLocation))
                                        {
                                            Directory.CreateDirectory(folderLocation);
                                        }
                                    }

                                    destinationStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 8192, FileOptions.DeleteOnClose);
                                    destinationStream.SetLength(ChunkPositionLengthDict[i].Length);
                                    destinationStream.Flush();
                                }

                                if (!File.Exists(fileName))
                                {
                                    throw new Exception("At this point the item data file should have been created. This exception should not really be possible.");
                                }
                            }
                            else
                            {
                                //If we are not exclusively building to the disk we just use a memory stream at this stage
                                destinationStream = new MemoryStream(ChunkPositionLengthDict[i].Length);
                            }

                            //Ensure we start at the beginning of the stream
                            destinationStream.Seek(0, SeekOrigin.Begin);

                            //Copy over the correct part of the itemDataStream to the chunks
                            StreamTools.Write(itemDataStream, ChunkPositionLengthDict[i].Position, ChunkPositionLengthDict[i].Length, destinationStream, 8192, double.MaxValue, int.MaxValue);
                            ChunkDataStreams[i] = new StreamTools.ThreadSafeStream(destinationStream);
                        }
                    }
                }
                #endregion

                this.CompleteDataCheckSum = StreamTools.MD5(itemDataStream);
            }
        }
Пример #3
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");
                }
            }
        }