/// <summary> /// Create a new connection object /// </summary> /// <param name="connectionInfo">ConnectionInfo corresponding to the new connection</param> /// <param name="defaultSendReceiveOptions">The SendReceiveOptions which should be used as connection defaults</param> protected Connection(ConnectionInfo connectionInfo, SendReceiveOptions defaultSendReceiveOptions) { //If the application layer protocol is disabled the serialiser must be NullSerializer //and no data processors are allowed. if (connectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Disabled) { if (defaultSendReceiveOptions.Options.ContainsKey("ReceiveConfirmationRequired")) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options specified the ReceiveConfirmationRequired option. Please provide compatible send receive options in order to successfully" + " instantiate this unmanaged connection.", "defaultSendReceiveOptions"); if (defaultSendReceiveOptions.DataSerializer != DPSManager.GetDataSerializer<NullSerializer>()) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options serialiser was not NullSerializer. Please provide compatible send receive options in order to successfully" + " instantiate this unmanaged connection.", "defaultSendReceiveOptions"); if (defaultSendReceiveOptions.DataProcessors.Count > 0) throw new ArgumentException("Attempted to create an unmanaged connection when the provided send receive" + " options contains data processors. Data processors may not be used with unmanaged connections." + " Please provide compatible send receive options in order to successfully instantiate this unmanaged connection.", "defaultSendReceiveOptions"); } SendTimesMSPerKBCache = new CommsMath(); packetBuilder = new PacketBuilder(); //Initialise the sequence counter using the global value //Subsequent values on this connection are guaranteed to be sequential packetSequenceCounter = Interlocked.Increment(ref NetworkComms.totalPacketSendCount); ConnectionInfo = connectionInfo; if (defaultSendReceiveOptions != null) ConnectionDefaultSendReceiveOptions = defaultSendReceiveOptions; else ConnectionDefaultSendReceiveOptions = NetworkComms.DefaultSendReceiveOptions; //Add any listener specific packet handlers if required if (connectionInfo.ConnectionListener != null) connectionInfo.ConnectionListener.AddListenerPacketHandlersToConnection(this); if (NetworkComms.commsShutdown) throw new ConnectionSetupException("Attempting to create new connection after global NetworkComms.Net shutdown has been initiated."); if (ConnectionInfo.ConnectionType == ConnectionType.Undefined || ConnectionInfo.RemoteEndPoint == null) throw new ConnectionSetupException("ConnectionType and RemoteEndPoint must be defined within provided ConnectionInfo."); //If a connection already exists with this info then we can throw an exception here to prevent duplicates if (NetworkComms.ConnectionExists(connectionInfo.RemoteEndPoint, connectionInfo.LocalEndPoint, connectionInfo.ConnectionType, connectionInfo.ApplicationLayerProtocol)) throw new ConnectionSetupException("A " + connectionInfo.ConnectionType.ToString() + " connection already exists with info " + connectionInfo); //We add a reference in the constructor to ensure any duplicate connection problems are picked up here NetworkComms.AddConnectionReferenceByRemoteEndPoint(this); }
/// <summary> /// Attempts to use the data provided in packetBuilder to recreate something useful. If we don't have enough data /// yet that value is set in packetBuilder. /// </summary> /// <param name="packetBuilder">The <see cref="PacketBuilder"/> containing incoming cached data</param> protected void IncomingPacketHandleHandOff(PacketBuilder packetBuilder) { int loopCounter = 0; try { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... checking for completed packet with " + packetBuilder.TotalBytesCached.ToString() + " bytes read."); if (packetBuilder.TotalPartialPacketCount == 0) throw new Exception("Executing IncomingPacketHandleHandOff when no packets exist in packetbuilder."); //Loop until we are finished with this packetBuilder while (true) { //If we have ended up with a null packet at the front, probably due to some form of concatenation we can pull it off here //It is possible we have concatenation of several null packets along with real data so we loop until the firstByte is greater than 0 if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled && packetBuilder.FirstByte() == 0) { #region Ignore Null Packet if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... null packet removed in IncomingPacketHandleHandOff() from " + ConnectionInfo + ", loop index - " + loopCounter.ToString()); packetBuilder.ClearNTopBytes(1); //Reset the expected bytes to 0 so that the next check starts from scratch packetBuilder.TotalBytesExpected = 0; //If we have run out of data completely then we can return immediately if (packetBuilder.TotalBytesCached == 0) return; #endregion } else { int packetHeaderSize = 0; PacketHeader topPacketHeader; #region Set topPacketHeader if (ConnectionInfo.ApplicationLayerProtocol == ApplicationLayerProtocolStatus.Enabled) { //First determine the expected size of a header packet packetHeaderSize = packetBuilder.FirstByte() + 1; //Do we have enough data to build a header? if (packetBuilder.TotalBytesCached < packetHeaderSize) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... require " + packetHeaderSize + " bytes for packet header, only " + packetBuilder.TotalBytesCached + " bytes cached."); //Set the expected number of bytes and then return packetBuilder.TotalBytesExpected = packetHeaderSize; return; } if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... deserializing header using " + packetHeaderSize + " bytes, " + packetBuilder.TotalBytesCached + " bytes cached."); //We have enough for a header using (MemoryStream headerStream = packetBuilder.ReadDataSection(1, packetHeaderSize - 1)) topPacketHeader = new PacketHeader(headerStream, NetworkComms.InternalFixedSendReceiveOptions); } else topPacketHeader = new PacketHeader(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged), packetBuilder.TotalBytesCached); #endregion //Idiot test if (topPacketHeader.PacketType == null) throw new SerialisationException("packetType value in packetHeader should never be null"); //We can now use the header to establish if we have enough payload data //First case is when we have not yet received enough data if (packetBuilder.TotalBytesCached < packetHeaderSize + topPacketHeader.TotalPayloadSize) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... more data required for complete packet payload. Expecting " + (packetHeaderSize + topPacketHeader.TotalPayloadSize).ToString() + " total packet bytes."); //Set the expected number of bytes and then return packetBuilder.TotalBytesExpected = packetHeaderSize + topPacketHeader.TotalPayloadSize; return; } //Second case is we have enough data else if (packetBuilder.TotalBytesCached >= packetHeaderSize + topPacketHeader.TotalPayloadSize) { #region Handle Packet //We can either have exactly the right amount or even more than we were expecting //We may have too much data if we are sending high quantities and the packets have been concatenated SendReceiveOptions incomingPacketSendReceiveOptions = IncomingPacketSendReceiveOptions(topPacketHeader); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Received packet of type '" + topPacketHeader.PacketType + "' from " + ConnectionInfo + ", containing " + packetHeaderSize.ToString() + " header bytes and " + topPacketHeader.TotalPayloadSize.ToString() + " payload bytes."); bool isReservedPacketType = (topPacketHeader.PacketType != Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Unmanaged) && NetworkComms.ReservedPacketTypeNames.ContainsKey(topPacketHeader.PacketType)); //Get the packet sequence number if logging string packetSeqNumStr = ""; if (NetworkComms.LoggingEnabled) packetSeqNumStr = (topPacketHeader.ContainsOption(PacketHeaderLongItems.PacketSequenceNumber) ? ". pSeq#-" + topPacketHeader.GetOption(PacketHeaderLongItems.PacketSequenceNumber).ToString() + "." : ""); //Only reserved packet types get completed inline by default if (isReservedPacketType) { #if WINDOWS_PHONE || NETFX_CORE QueueItemPriority priority = QueueItemPriority.Normal; #else QueueItemPriority priority = (QueueItemPriority)Thread.CurrentThread.Priority; #endif PriorityQueueItem item = new PriorityQueueItem(priority, this, topPacketHeader, packetBuilder.ReadDataSection(packetHeaderSize, topPacketHeader.TotalPayloadSize), incomingPacketSendReceiveOptions); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... handling packet type '" + topPacketHeader.PacketType + "' inline. Loop index - " + loopCounter.ToString() + packetSeqNumStr); NetworkComms.CompleteIncomingItemTask(item); } else { QueueItemPriority itemPriority = (incomingPacketSendReceiveOptions.Options.ContainsKey("ReceiveHandlePriority") ? (QueueItemPriority)Enum.Parse(typeof(QueueItemPriority), incomingPacketSendReceiveOptions.Options["ReceiveHandlePriority"]) : QueueItemPriority.Normal); PriorityQueueItem item = new PriorityQueueItem(itemPriority, this, topPacketHeader, packetBuilder.ReadDataSection(packetHeaderSize, topPacketHeader.TotalPayloadSize), incomingPacketSendReceiveOptions); //QueueItemPriority.Highest is the only priority that is executed inline if (itemPriority == QueueItemPriority.Highest) { if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... handling packet type '" + topPacketHeader.PacketType + "' with priority HIGHEST inline. Loop index - " + loopCounter.ToString() + packetSeqNumStr); NetworkComms.CompleteIncomingItemTask(item); } else { #if NETFX_CORE NetworkComms.CommsThreadPool.EnqueueItem(item.Priority, NetworkComms.CompleteIncomingItemTask, item); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... added completed " + item.PacketHeader.PacketType + " packet to thread pool (Q:" + NetworkComms.CommsThreadPool.QueueCount.ToString() + ") with priority " + itemPriority.ToString() + ". Loop index=" + loopCounter.ToString() + packetSeqNumStr); #else int threadId = NetworkComms.CommsThreadPool.EnqueueItem(item.Priority, NetworkComms.CompleteIncomingItemTask, item); if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace(" ... added completed " + item.PacketHeader.PacketType + " packet to thread pool (Q:" + NetworkComms.CommsThreadPool.QueueCount.ToString() + ", T:" + NetworkComms.CommsThreadPool.CurrentNumTotalThreads.ToString() + ", I:" + NetworkComms.CommsThreadPool.CurrentNumIdleThreads.ToString() + ") with priority " + itemPriority.ToString() + (threadId > 0 ? ". Selected threadId=" + threadId.ToString() : "") + ". Loop index=" + loopCounter.ToString() + packetSeqNumStr); #endif } } //We clear the bytes we have just handed off if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Removing " + (packetHeaderSize + topPacketHeader.TotalPayloadSize).ToString() + " bytes from incoming packet builder from connection with " + ConnectionInfo +"."); packetBuilder.ClearNTopBytes(packetHeaderSize + topPacketHeader.TotalPayloadSize); //Reset the expected bytes to 0 so that the next check starts from scratch packetBuilder.TotalBytesExpected = 0; //If we have run out of data completely then we can return immediately if (packetBuilder.TotalBytesCached == 0) return; #endregion } else throw new CommunicationException("This should be impossible!"); } loopCounter++; } } catch (Exception ex) { //Any error, throw an exception. if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Fatal("A fatal exception occurred in IncomingPacketHandleHandOff(), connection with " + ConnectionInfo + " be closed. See log file for more information."); if (this is IPConnection) { //Log the exception in DOS protection if enabled if (IPConnection.DOSProtection.Enabled && ConnectionInfo.RemoteEndPoint.GetType() == typeof(IPEndPoint)) IPConnection.DOSProtection.LogMalformedData(ConnectionInfo.RemoteIPEndPoint.Address); } LogTools.LogException(ex, "CommsError", "A fatal exception occurred in IncomingPacketHandleHandOff(), connection with " + ConnectionInfo + " be closed. Loop counter " + loopCounter.ToString() + ". Packet builder contained " + packetBuilder.TotalBytesCached + " total cached bytes."); CloseConnection(true, 45); } }