protected override void OnDataReceived(byte[] data)
{
const int packetLengthByteCount = 4;
const int sftpMessageTypeByteCount = 1;
const int minimumChannelDataLength = packetLengthByteCount + sftpMessageTypeByteCount;
var offset = 0;
var count = data.Length;
// improve performance and reduce GC pressure by not buffering channel data if the received
// chunk contains the complete packet data.
//
// for this, the buffer should be empty and the chunk should contain at least the packet length
// and the type of the SFTP message
if (_data.Count == 0)
{
while (count >= minimumChannelDataLength)
{
// extract packet length
var packetDataLength = data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 |
data[offset + 3];
var packetTotalLength = packetDataLength + packetLengthByteCount;
// check if complete packet data (or more) is available
if (count >= packetTotalLength)
{
// load and process SFTP message
if (!TryLoadSftpMessage(data, offset + packetLengthByteCount, packetDataLength))
{
return;
}
// remove processed bytes from the number of bytes to process as the channel
// data we received may contain (part of) another message
count -= packetTotalLength;
// move offset beyond bytes we just processed
offset += packetTotalLength;
}
else
{
// we don't have a complete message
break;
}
}
// check if there is channel data left to process or buffer
if (count == 0)
{
return;
}
// check if we processed part of the channel data we received
if (offset > 0)
{
// add (remaining) channel data to internal data holder
var remainingChannelData = new byte[count];
Buffer.BlockCopy(data, offset, remainingChannelData, 0, count);
_data.AddRange(remainingChannelData);
}
else
{
// add (remaining) channel data to internal data holder
_data.AddRange(data);
}
// skip further processing as we'll need a new chunk to complete the message
return;
}
// add (remaining) channel data to internal data holder
_data.AddRange(data);
while (_data.Count >= minimumChannelDataLength)
{
// extract packet length
var packetDataLength = _data[0] << 24 | _data[1] << 16 | _data[2] << 8 | _data[3];
var packetTotalLength = packetDataLength + packetLengthByteCount;
// check if complete packet data is available
if (_data.Count < packetTotalLength)
{
// wait for complete message to arrive first
break;
}
// create buffer to hold packet data
var packetData = new byte[packetDataLength];
// copy packet data and bytes for length to array
_data.CopyTo(packetLengthByteCount, packetData, 0, packetDataLength);
// remove loaded data and bytes for length from _data holder
if (_data.Count == packetTotalLength)
{
// the only buffered data is the data we're processing
_data.Clear();
}
else
{
// remove only the data we're processing
_data.RemoveRange(0, packetTotalLength);
}
// load and process SFTP message
if (!TryLoadSftpMessage(packetData, 0, packetDataLength))
{
break;
}
}
}