bool ReceiveEFTResponse(byte[] data) { // Clear the receive buffer if 5 seconds has lapsed since the last message var tc = System.Environment.TickCount; if (tc - recvTickCount > 5000) { Log(LogLevel.Debug, tr => tr.Set($"Data is being cleared from the buffer due to a timeout. Content {recvBuf.ToString()}")); recvBuf = ""; } recvTickCount = System.Environment.TickCount; // Append receive data to our buffer recvBuf += System.Text.Encoding.ASCII.GetString(data, 0, data.Length); // Keep parsing until no more characters try { int index = 0; while (index < recvBuf.Length) { // Look for start character if (recvBuf[index] == (byte)'#') { // Check that we have enough bytes to validate length, if not wait for more if (recvBuf.Length < index + 5) { recvBuf = recvBuf.Substring(index); break; } // We have enough bytes to check for length index++; // Try to get the length of the new message. If it's not a valid length // we might have some corrupt data, keep checking for a valid message if (!int.TryParse(recvBuf.Substring(index, 4), out int length) || length <= 5) { continue; } // We have a valid length index += 4; // If our buffer doesn't contain enough data, wait for more if (recvBuf.Length < index + length - 5) { recvBuf = recvBuf.Substring(index - 5); continue; } // We have a valid response var response = recvBuf.Substring(index, length - 5); FireOnTcpReceive(response); // Process the response EFTResponse eftResponse = null; try { eftResponse = _parser.StringToEFTResponse(response); ProcessEFTResponse(eftResponse); if (eftResponse.GetType() == _currentStartTxnRequest?.GetPairedResponseType()) { dialogUIHandler.HandleCloseDisplay(); } } catch (ArgumentException argumentException) { Log(LogLevel.Error, tr => tr.Set("Error parsing response string", argumentException)); } index += length - 5; } // Clear our buffer if we are all done if (index == recvBuf.Length) { recvBuf = ""; } } } catch (Exception ex) { // Fail gracefully. FireOnSocketFailEvent(EFTClientIPErrorType.Client_ParseError, ex.Message); Log(LogLevel.Error, tr => tr.Set($"Exception (ReceiveEFTResponse): {ex.Message}", ex)); return(false); } return(true); }
/// <summary> /// Retrieves the next EFT response from the client /// </summary> /// <param name="cancellationToken">The token to monitor for cancellation requests</param> /// <returns> An EFTResponse if one could be read, otherwise null </returns> /// <exception cref="ConnectionException">The socket is closed.</exception> /// <exception cref="TaskCanceledException">The task was cancelled by cancellationToken</exception> public async Task <EFTResponse> ReadResponseAsync(System.Threading.CancellationToken cancellationToken) { // Clear the receive buffer if we have been waiting for more than // 5 seconds for the remaining parts of the message to arrive var tc = System.Environment.TickCount; if (_recvBufWaiting && tc - _recvTickCount > 5000 && _recvBuf.Length > 0) { Log(LogLevel.Debug, tr => tr.Set($"Data is being cleared from the buffer due to a timeout. Content {_recvBuf.ToString()}")); _recvBufWaiting = false; _recvBuf.Clear(); } // Only try to read more data if our receive buffer is empty Log(LogLevel.Debug, tr => tr.Set($"ReadResponseAsync() _recvBuf.Length={_recvBuf.Length} _recvBufWaiting={_recvBufWaiting}")); if (_recvBuf.Length == 0 || _recvBufWaiting == true) { var bytesRead = 0; try { bytesRead = await _clientStream.ReadResponseAsync(_buffer, cancellationToken); _recvTickCount = System.Environment.TickCount; } catch (TaskCanceledException e) { Log(LogLevel.Debug, tr => tr.Set($"Socket read cancelled", e)); throw e; } catch (Exception e) { Log(LogLevel.Error, tr => tr.Set($"An error occured sending the request", e)); Disconnect(); throw new ConnectionException(e.Message, e.InnerException); } // Check for socket closed if (bytesRead <= 0) { Log(LogLevel.Error, tr => tr.Set("Recv 0 bytes. Socket closed")); Disconnect(); throw new ConnectionException("Socket closed."); } var msgString = DirectEncoding.DIRECT.GetString(_buffer, 0, bytesRead); Log(LogLevel.Info, tr => tr.Set($"Rx {msgString}")); // Append receive data to our buffer _recvBuf.Append(msgString); } // Keep parsing until no more characters try { bool isXMLFormatted = false; int index = 0; while (index < _recvBuf.Length) { // If the current char isn't #/& then keep cycling through the message until we find one if (_recvBuf[index] != (byte)'#' && !(isXMLFormatted = _recvBuf[index] == (byte)'&')) { index++; continue; } // We have a valid start char, check for length. index++; int lengthSize = isXMLFormatted ? 6 : 4; // Check that we have enough bytes to validate length, if not wait for more if (_recvBuf.Length < index + lengthSize) { Log(LogLevel.Debug, tr => tr.Set($"Unable to validate message header. Waiting for more data. Length:{_recvBuf.Length} Required:{index + lengthSize + 1}")); _recvBufWaiting = true; break; } // Try to get the length of the new message. If it's not a valid length // we might have some corrupt data, keep checking for a valid message var lengthStr = _recvBuf.Substring(index, lengthSize); if (!int.TryParse(lengthStr, out int length) || length <= (lengthSize + 1)) { Log(LogLevel.Error, tr => tr.Set($"Invalid length. Content:{lengthStr}")); continue; } // We have a valid length index += lengthSize; // If the defined message length is > our current buffer size, wait for more data if (_recvBuf.Length < index + length - (lengthSize + 1)) { Log(LogLevel.Debug, tr => tr.Set($"Buffer is less than the indicate length. Waiting for more data. Length:{_recvBuf.Length < index} Required:{length - (lengthSize + 1)}")); _recvBuf = _recvBuf.Remove(0, index - (lengthSize + 1)); _recvBufWaiting = true; break; } // We have a valid response _recvBufWaiting = false; var response = _recvBuf.Substring(index, length - (lengthSize + 1)); index += (length - (lengthSize + 1)); _recvBuf.Remove(0, index); // Process the response EFTResponse eftResponse = null; try { //if (response.Equals("3M") || response.Equals("3C")) // FOR PREPRINT TIMEOUT //if (response.Substring(0,2).Equals("3R")) // FOR PRINT TIMEOUT // System.Threading.Thread.Sleep(61000); eftResponse = (isXMLFormatted) ? _parser.XMLStringToEFTResponse(response) : _parser.StringToEFTResponse(response); // If we have an EFTResponse we need to return it. if (eftResponse != null) { // Print requests need a response if (eftResponse is EFTReceiptResponse) { await WriteRequestAsync(new EFTReceiptRequest()); } //DialogUIHandler needs to be notified of display messages else if (eftResponse is EFTDisplayResponse && DialogUIHandlerAsync != null) { await DialogUIHandlerAsync.HandleDisplayResponseAsync(eftResponse as EFTDisplayResponse); } else if (eftResponse.GetType() == _currentStartTxnRequest?.GetPairedResponseType() && DialogUIHandlerAsync != null) { await DialogUIHandlerAsync.HandleCloseDisplayAsync(); } return(eftResponse); } } catch (Exception e) { Log(LogLevel.Error, tr => tr.Set("Error parsing response string", e)); } } // Clear our buffer if we are all done (this shouldn't happen) if (index == _recvBuf.Length) { _recvBufWaiting = false; _recvBuf.Clear(); } } catch (Exception ex) { Log(LogLevel.Error, tr => tr.Set($"Exception (ReceiveEFTResponse): {ex.Message}", ex)); throw ex; } return(null); }