I need some help figuring out how to troubleshoot a problem I am witnessing with a high-volume data feed over TCP using .NET sockets.
In a nutshell, when the client application starts, it connects to a specific port on the server. Once connected, the server begins sending real-time data to the client which displays the information in a ticker-like UI. The server supports multiple client workstations, so data will be sent via multiple ports (multiple sockets).
Everything is implemented and working great with a slow feed and low-volume. I am stress testing the system to ensure resiliance and scalability. When I increase the frequency, the server runs perfectly. However, I am seeing what appears to be lost packets on the clients. This occurs at random times.
Currently, each message that is broadcast is prefaced with a 4-byte value identifying the length of that message. As we are receiving data in the client, we append the data to a buffer (Stream) until we receive that number of bytes. Any additional bytes are considered the start of the next message. Again, this works great until I turn up the frequency.
In my test, I send a packet of approx 225 bytes, followed by one of approx 310kB and another around 40kb. Sending a message every 1 second works without fail with about 12 clients running. Increasing the frequency to 1/2 second, I eventually saw one of the client's displays freeze. Going to 1/4 second and I can reproduce the problem with as few as 4 clients within a few seconds.
Looking at my code (which I can provide, if needed), I see that all of the clients are receiving data but somehow the information fell 'out-of-sync' and the expected length value is enormous (in the 100 million range). As a result, we just keep reading data and never perceive the end of the message.
I either need a better approach or a way to ensure I'm getting the data I expect and not losing packets. Can you help?
UPDATE
I've done a ton of additional testing, varying the size of the messages and delivery frequency. There is definitely a correlation. The smaller I make the message sizes, the higher the frequency I can achieve. But, inevitably, I am always able to break it.
So, to more accurately describe what I am looking for is:
To understand what is happening. This will help me identify a possible solution or, at a minimum, establish thresholds for reliable behavior.
Implement a fail-safe mechanism so when the problem occurs, I can handle it and possibly recover from it. Perhaps adding a checksum into the data stream or something like that.
Here is the code that I am running in the client (receiving) applications:
public void StartListening(SocketAsyncEventArgs e)
{
e.Completed += SocketReceive;
socket.ReceiveAsync(e);
}
private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
lock (_receiveLock)
{
ProcessData(e.Buffer, e.BytesTransferred);
socket.ReceiveAsync(e);
}
}
private void ProcessData(Byte[] bytes, Int32 count)
{
if (_currentBuffer == null)
_currentBuffer = new ReceiveBuffer();
var numberOfBytesRead = _currentBuffer.Write(bytes, count);
if (_currentBuffer.IsComplete)
{
// Notify the client that a message has been received (ignore zero-length, "keep alive", messages)
if (_currentBuffer.DataLength > 0)
NotifyMessageReceived(_currentBuffer);
_currentBuffer = null;
// If there are bytes remaining from the original message, recursively process
var numberOfBytesRemaining = count - numberOfBytesRead;
if (numberOfBytesRemaining > 0)
{
var remainingBytes = new Byte[numberOfBytesRemaining];
var offset = bytes.Length - numberOfBytesRemaining;
Array.Copy(bytes, offset, remainingBytes, 0, numberOfBytesRemaining);
ProcessData(remainingBytes, numberOfBytesRemaining);
}
}
}
internal sealed class ReceiveBuffer
{
public const Int32 LengthBufferSize = sizeof(Int32);
private MemoryStream _dataBuffer = new MemoryStream();
private MemoryStream _lengthBuffer = new MemoryStream();
public Int32 DataLength { get; private set; }
public Boolean IsComplete
{
get { return (RemainingDataBytesToWrite == 0); }
}
private Int32 RemainingDataBytesToWrite
{
get
{
if (DataLength > 0)
return (DataLength - (Int32)_dataBuffer.Length);
return 0;
}
}
private Int32 RemainingLengthBytesToWrite
{
get { return (LengthBufferSize - (Int32)_lengthBuffer.Length); }
}
public Int32 Write(Byte[] bytes, Int32 count)
{
var numberOfLengthBytesToWrite = Math.Min(RemainingLengthBytesToWrite, count);
if (numberOfLengthBytesToWrite > 0)
WriteToLengthBuffer(bytes, numberOfLengthBytesToWrite);
var remainingCount = count - numberOfLengthBytesToWrite;
// If this value is > 0, then we have still have more bytes after setting the length so write them to the data buffer
var numberOfDataBytesToWrite = Math.Min(RemainingDataBytesToWrite, remainingCount);
if (numberOfDataBytesToWrite > 0)
_dataBuffer.Write(bytes, numberOfLengthBytesToWrite, numberOfDataBytesToWrite);
return numberOfLengthBytesToWrite + numberOfDataBytesToWrite;
}
private void WriteToLengthBuffer(Byte[] bytes, Int32 count)
{
_lengthBuffer.Write(bytes, 0, count);
if (RemainingLengthBytesToWrite == 0)
{
var length = BitConverter.ToInt32(_lengthBuffer.ToArray(), 0);
DataLength = length;
}
}
}