/// <summary> /// Sends the specified packet to this node. /// </summary> /// <param name="packet">The packet to send.</param> public void SendData(INodePacket packet) { MemoryStream writeStream = new MemoryStream(); ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(writeStream); try { writeStream.WriteByte((byte)packet.Type); // Pad for the packet length writeStream.Write(BitConverter.GetBytes((int)0), 0, 4); packet.Translate(writeTranslator); // Now plug in the real packet length writeStream.Position = 1; writeStream.Write(BitConverter.GetBytes((int)writeStream.Length - 5), 0, 4); byte[] writeStreamBuffer = writeStream.GetBuffer(); for (int i = 0; i < writeStream.Length; i += MaxPacketWriteSize) { int lengthToWrite = Math.Min((int)writeStream.Length - i, MaxPacketWriteSize); if ((int)writeStream.Length - i <= MaxPacketWriteSize) { // We are done, write the last bit asynchronously. This is actually the general case for // most packets in the build, and the asynchronous behavior here is desirable. #if FEATURE_APM _serverToClientStream.BeginWrite(writeStreamBuffer, i, lengthToWrite, PacketWriteComplete, null); #else _serverToClientStream.WriteAsync(writeStreamBuffer, i, lengthToWrite); #endif return; } else { // If this packet is longer that we can write in one go, then we need to break it up. We can't // return out of this function and let the rest of the system continue because another operation // might want to send data immediately afterward, and that could result in overlapping writes // to the pipe on different threads. #if FEATURE_APM IAsyncResult result = _serverToClientStream.BeginWrite(writeStream.GetBuffer(), i, lengthToWrite, null, null); _serverToClientStream.EndWrite(result); #else _serverToClientStream.Write(writeStreamBuffer, i, lengthToWrite); #endif } } } catch (IOException e) { // Do nothing here because any exception will be caught by the async read handler CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in SendData: {0}", e); } catch (ObjectDisposedException) // This happens if a child dies unexpectedly { // Do nothing here because any exception will be caught by the async read handler } }
/// <summary> /// Actually writes and sends the packet. This can't be called in parallel /// because it reuses the _writeBufferMemoryStream, and this is why we use /// the _packetWriteDrainTask to serially chain invocations one after another. /// </summary> /// <param name="packet">The packet to send.</param> private void SendDataCore(INodePacket packet) { MemoryStream writeStream = _writeBufferMemoryStream; // clear the buffer but keep the underlying capacity to avoid reallocations writeStream.SetLength(0); ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(writeStream); try { writeStream.WriteByte((byte)packet.Type); // Pad for the packet length WriteInt32(writeStream, 0); packet.Translate(writeTranslator); int writeStreamLength = (int)writeStream.Position; // Now plug in the real packet length writeStream.Position = 1; WriteInt32(writeStream, writeStreamLength - 5); byte[] writeStreamBuffer = writeStream.GetBuffer(); for (int i = 0; i < writeStreamLength; i += MaxPacketWriteSize) { int lengthToWrite = Math.Min(writeStreamLength - i, MaxPacketWriteSize); _serverToClientStream.Write(writeStreamBuffer, i, lengthToWrite); } if (IsExitPacket(packet)) { _exitPacketState = ExitPacketState.ExitPacketSent; } } catch (IOException e) { // Do nothing here because any exception will be caught by the async read handler CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in SendData: {0}", e); } catch (ObjectDisposedException) // This happens if a child dies unexpectedly { // Do nothing here because any exception will be caught by the async read handler } }
private bool ReadAndRoutePacket(NodePacketType packetType, byte [] packetData, int packetLength) { try { // The buffer is publicly visible so that InterningBinaryReader doesn't have to copy to an intermediate buffer. // Since the buffer is publicly visible dispose right away to discourage outsiders from holding a reference to it. using (var packetStream = new MemoryStream(packetData, 0, packetLength, /*writeable*/ false, /*bufferIsPubliclyVisible*/ true)) { ITranslator readTranslator = BinaryTranslator.GetReadTranslator(packetStream, _sharedReadBuffer); _packetFactory.DeserializeAndRoutePacket(_nodeId, packetType, readTranslator); } } catch (IOException e) { CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in ReadAndRoutPacket: {0}", e); _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); Close(); return(false); } return(true); }
private void RunReadLoop(Stream localReadPipe, Stream localWritePipe, ConcurrentQueue <INodePacket> localPacketQueue, AutoResetEvent localPacketAvailable, AutoResetEvent localTerminatePacketPump) { // Ordering of the wait handles is important. The first signalled wait handle in the array // will be returned by WaitAny if multiple wait handles are signalled. We prefer to have the // terminate event triggered so that we cannot get into a situation where packets are being // spammed to the endpoint and it never gets an opportunity to shutdown. CommunicationsUtilities.Trace("Entering read loop."); byte[] headerByte = new byte[5]; #if FEATURE_APM IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #else Task <int> readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); #endif bool exitLoop = false; do { // Ordering is important. We want packetAvailable to supercede terminate otherwise we will not properly wait for all // packets to be sent by other threads which are shutting down, such as the logging thread. WaitHandle[] handles = new WaitHandle[] { #if FEATURE_APM result.AsyncWaitHandle, #else ((IAsyncResult)readTask).AsyncWaitHandle, #endif localPacketAvailable, localTerminatePacketPump }; int waitId = WaitHandle.WaitAny(handles); switch (waitId) { case 0: { int bytesRead = 0; try { #if FEATURE_APM bytesRead = localReadPipe.EndRead(result); #else bytesRead = readTask.Result; #endif } catch (Exception e) { // Lost communications. Abort (but allow node reuse) CommunicationsUtilities.Trace("Exception reading from server. {0}", e); ExceptionHandling.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Inactive); exitLoop = true; break; } if (bytesRead != headerByte.Length) { // Incomplete read. Abort. if (bytesRead == 0) { CommunicationsUtilities.Trace("Parent disconnected abruptly"); } else { CommunicationsUtilities.Trace("Incomplete header read from server. {0} of {1} bytes read", bytesRead, headerByte.Length); } ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; break; } NodePacketType packetType = (NodePacketType)Enum.ToObject(typeof(NodePacketType), headerByte[0]); try { _packetFactory.DeserializeAndRoutePacket(0, packetType, BinaryTranslator.GetReadTranslator(localReadPipe, _sharedReadBuffer)); } catch (Exception e) { // Error while deserializing or handling packet. Abort. CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e); ExceptionHandling.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; break; } #if FEATURE_APM result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #else readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); #endif } break; case 1: case 2: try { // Write out all the queued packets. INodePacket packet; while (localPacketQueue.TryDequeue(out packet)) { MemoryStream packetStream = new MemoryStream(); ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(packetStream); packetStream.WriteByte((byte)packet.Type); // Pad for packet length packetStream.Write(BitConverter.GetBytes((int)0), 0, 4); // Reset the position in the write buffer. packet.Translate(writeTranslator); // Now write in the actual packet length packetStream.Position = 1; packetStream.Write(BitConverter.GetBytes((int)packetStream.Length - 5), 0, 4); localWritePipe.Write(packetStream.GetBuffer(), 0, (int)packetStream.Length); } } catch (Exception e) { // Error while deserializing or handling packet. Abort. CommunicationsUtilities.Trace("Exception while serializing packets: {0}", e); ExceptionHandling.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; break; } if (waitId == 2) { CommunicationsUtilities.Trace("Disconnecting voluntarily"); ChangeLinkStatus(LinkStatus.Failed); exitLoop = true; } break; default: ErrorUtilities.ThrowInternalError("waitId {0} out of range.", waitId); break; } }while (!exitLoop); }