/// <summary> /// Returns the number of bytes to read from the stream. /// </summary> /// <param name="state">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param> /// <param name="reader"><see cref="T:SharpStructures.BinaryListReader" /> linked to a FLEX message body.</param> /// <param name="writer"><see cref="T:SharpStructures.MemoryBufferWriter" /> with data to be sent to the client.</param> /// <param name="token">The token to monitor for cancellation requests.</param> /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns> public async Task <int> GetPendingBytesAsync(SessionState state, BinaryListReader reader, MemoryBufferWriter writer, CancellationToken token) { switch (reader.ByteList.Count) { case 2: return(13); case 15: reader.SetPosition(13); return(reader.ReadUInt16() + 1); default: if (reader.ByteList.Last() != BinaryUtilities.GetCrc8(reader.ByteList.Take(reader.ByteList.Count - 1))) { throw new ArgumentException("Invalid FLEX message CRC."); } reader.SetPosition(4); var result = reader.ReadByte(); var crashTime = reader.ReadUInt32(); var offset = reader.ReadUInt32(); var sizeRead = reader.ReadUInt16(); if (state.CrashInfo != null && result == 0x00 && state.CrashInfo.Timestamp == crashTime) { for (var i = 0; i < sizeRead; ++i) { state.CrashInfo.Data[offset + i] = reader.ByteList[15 + i]; } state.CrashInfo.Offset = offset + sizeRead; var bytesLeft = state.CrashInfo.Data.Length - state.CrashInfo.Offset; writer.Write(CrashInformation.CrashDataQuery); writer.Write(crashTime); writer.Write(state.CrashInfo.Offset); writer.Write(bytesLeft > ushort.MaxValue ? ushort.MaxValue : (ushort)bytesLeft); writer.Write(BinaryUtilities.GetCrc8(writer.Buffer)); if (bytesLeft == 0) { if (_onReadyCrash != null) { await _onReadyCrash(state, token).ConfigureAwait(false); } state.CrashInfo = null; } } return(0); } }
/// <summary> /// Returns the number of bytes to read from the stream. /// </summary> /// <param name="sessionState">Instance of <see cref="T:NavtelecomProtocol.SessionState" />.</param> /// <param name="receiveBuffer">A read-only collection of bytes received from a stream.</param> /// <param name="sendBuffer"><see cref="T:SharpStructures.MemoryBuffer" /> with data to be sent to the client.</param> /// <param name="token">The token to monitor for cancellation requests.</param> /// <returns>A task that represents the asynchronous operation. Contains the total number of bytes to read from the socket. Zero bytes to stop reading and send the response.</returns> public Task <int> GetPendingBytesAsync(SessionState sessionState, IReadOnlyList <byte> receiveBuffer, MemoryBuffer sendBuffer, CancellationToken token) { switch (receiveBuffer.Count) { case 1: return(Task.FromResult(HeaderLength - 1)); case HeaderLength: var reader = new BinaryListReader(receiveBuffer, true); if (!reader.ReadBytes(HeaderPreamble.Length).Select(x => (char)x).SequenceEqual(HeaderPreamble)) { throw new ArgumentException("NTCB header preamble does not match."); } sessionState.ReceiverId = reader.ReadUInt32(); sessionState.SenderId = reader.ReadUInt32(); var payloadLength = reader.ReadUInt16(); reader.ReadByte(); var headerChecksum = reader.ReadByte(); if (BinaryUtilities.GetXorSum(receiveBuffer.Take(HeaderLength - 1)) != headerChecksum) { throw new ArgumentException("NTCB header checksum does not match."); } return(Task.FromResult((int)payloadLength)); default: if (BinaryUtilities.GetXorSum(receiveBuffer.Skip(HeaderLength)) != receiveBuffer[14]) { throw new ArgumentException("NTCB body checksum does not match."); } var bodyProcessor = _bodyProcessors.GetValueOrDefault(receiveBuffer.Skip(HeaderLength).Select(x => (char)x)); if (bodyProcessor == null) { throw new ArgumentException("Unknown NTCB message type."); } var bodyReader = new BinaryListReader(receiveBuffer, true); bodyReader.SetPosition(HeaderLength); sendBuffer.AllocateSpace(HeaderLength); var writer = new MemoryBufferWriter(sendBuffer, true); bodyProcessor.ProcessBody(sessionState, bodyReader, writer); var responseLength = sendBuffer.Position - HeaderLength; sendBuffer.SetPosition(0); writer.Write(HeaderPreamble.Select(x => (byte)x).ToArray()); writer.Write(sessionState.SenderId); writer.Write(sessionState.ReceiverId); writer.Write((ushort)responseLength); writer.Write(BinaryUtilities.GetXorSum(sendBuffer.Array.Skip(HeaderLength).Take(responseLength))); writer.Write(BinaryUtilities.GetXorSum(sendBuffer.Array.Take(HeaderLength - 1))); sendBuffer.SetPosition(responseLength + HeaderLength); return(Task.FromResult(0)); } }
/// <summary> /// Processes the established client-server socket connection. /// </summary> /// <param name="socket">Socket connected to a client device.</param> /// <param name="token">The token to monitor for cancellation requests.</param> /// <returns>A task that represents the asynchronous operation.</returns> public async Task WorkAsync(Socket socket, CancellationToken token) { using (var stream = new AsyncBufferedSocketStream(socket, token)) { stream.ReadTimeout = stream.WriteTimeout = _readWriteTimeoutMs; try { var state = new SessionState(); for (;;) { stream.ClearReceiveBuffer(); await stream.ReadToBufferAsync(1, token).ConfigureAwait(false); var processor = _primaryOperators[stream.ReceiveBuffer[0]]; if (processor == null) { throw new ArgumentException($"Unknown message prefix '0x{stream.ReceiveBuffer[0]:X2}'."); } stream.SendBuffer.SetPosition(0); for (;;) { var pendingBytes = await processor.GetPendingBytesAsync(state, stream.ReceiveBuffer, stream.SendBuffer, token) .ConfigureAwait(false); if (pendingBytes == 0) { break; } await stream.ReadToBufferAsync(pendingBytes, token).ConfigureAwait(false); } if (OnReadyMessage != null) { await OnReadyMessage(state, stream.ReceiveBuffer.ToArray(), token).ConfigureAwait(false); } await stream.WriteBufferToSocketAsync(token).ConfigureAwait(false); // Workaround until crashes are implemented correctly by the devices var crashDetected = false; if (processor.MessageTypeIdentifier == 0x40 && stream.ReceiveBuffer.Count >= 22 && stream.ReceiveBuffer.Skip(16).Take(6).Select(x => (char)x).SequenceEqual("*>FLEX")) { crashDetected = true; } else if (processor.MessageTypeIdentifier == 0x7E && stream.ReceiveBuffer.Count >= 2) { var reader = new BinaryListReader(stream.ReceiveBuffer, true); const int detectEvent = 0xA03B; switch ((char)stream.ReceiveBuffer[1]) { case 'T': if (stream.ReceiveBuffer.Count < 12) { break; } reader.SetPosition(10); var numType = reader.ReadUInt16(); if (numType == detectEvent) { crashDetected = true; } break; case 'A': if (stream.ReceiveBuffer.Count < 4) { break; } var eventCount = stream.ReceiveBuffer[2]; if (eventCount <= 0) { break; } var eventLength = (stream.ReceiveBuffer.Count - 4) / eventCount; if (eventLength < 6) { break; } for (var i = 0; i < eventCount; ++i) { reader.SetPosition(7 + i * eventLength); var eventType = reader.ReadUInt16(); if (eventType != detectEvent) { continue; } crashDetected = true; break; } break; } } if (crashDetected) { stream.SendBuffer.SetPosition(0); new MemoryBufferWriter(stream.SendBuffer, true).Write(CrashInformation.CrashInfoQuery); await stream.WriteBufferToSocketAsync(token).ConfigureAwait(false); } } } catch (SocketStreamException) { } catch (OperationCanceledException) { throw; } catch (Exception ex) { string receivedBytes = null; if (await stream.TryReadAvailableBytesAsync().ConfigureAwait(false)) { var hex = new StringBuilder(stream.ReceiveBuffer.Count * 2); foreach (var b in stream.ReceiveBuffer) { hex.Append(b.ToString("X2")); } receivedBytes = hex.ToString(); } var exceptionMessage = $"Error: {ex.Message}. Receive buffer: {receivedBytes ?? "NA"}."; throw new Exception(exceptionMessage, ex); } } }