.Net Sockets - TcpClient missing messages

Started by
8 comments, last by CyberSlag5k 15 years, 4 months ago
I'm using .Net Sockets to handle communications between my server and my client. Sometimes, though, the receiving TcpClient misses a transmissions. It happens on and off, but when it does happen, it happens consistently. It seems to happen most often when I'm sending 3 messages (packets?) in rapid succession, which makes me think that the way that I'm handling the messages on the client side (I'm pretty sure the server is sending them properly) incorrectly. This is the code that I use to handle the incoming messages:

if (m_Client.Connected == true) // m_Client is of type System.Net.Sockets.TcpClient
{
	NetworkStream stream = m_Client.GetStream();

	Byte[] buffer = new Byte[100000];

	Int32 count = stream.Read(buffer, 0, buffer.Length);
	while (count > 0)
	{
		Byte[] receivedBytes = new Byte[count];
		Buffer.BlockCopy(buffer, 0, receivedBytes, 0, count);

		lock (m_ReceivedQueue)
		{
			m_ReceivedQueue.Enqueue(receivedBytes);
		}

		count = stream.Read(buffer, 0, buffer.Length);
	}
}

What's going on is that I set up the TcpClient to wait for a message, and when one comes in, I stick the message on to a queue, which processes the message, and then wait for a new message. The queue shouldn't be locked for long, as this is what I do with it (in another thread):

Byte[] bytes = null;

lock (m_ReceivedQueue)
{
	if (m_ReceivedQueue.Count > 0)
	{
		bytes = m_ReceivedQueue.Dequeue();
	}
}

if (bytes != null)
{
// Do stuff
}

In both cases, I've tried to lessen the time that the queue stays locked, and I think I have that down to a minimum, but I'm still experiencing the missed messages. I've even tried moving the lock stuff into a background worker thread in the listening code:

if (m_Client.Connected == true)
{
	NetworkStream stream = m_Client.GetStream();

	Byte[] buffer = new Byte[100000];

	Int32 count = stream.Read(buffer, 0, buffer.Length);
	while (count > 0)
	{
		Byte[] receivedBytes = new Byte[count];
		Buffer.BlockCopy(buffer, 0, receivedBytes, 0, count);

		BackgroundWorker backgroundWorker = new BackgroundWorker();
		backgroundWorker.DoWork += delegate
		{
			lock (m_ReceivedQueue)
			{
				m_ReceivedQueue.Enqueue(receivedBytes);
			}
		};
		backgroundWorker.RunWorkerAsync();
		
		count = stream.Read(buffer, 0, buffer.Length);
	}
}

But that didn't help, either. I'm sending 3 messages, and the first and second get received just fine, but the third just isn't getting noticed by the client. Is my network model flawed? Should I be using WCF or something? Do the things I've tried suggest that maybe the problem is elsewhere? Thank you.
Without order nothing can exist - without chaos nothing can evolve.
Advertisement
What is: m_ReceivedQueue?

Do you treat buffer as part of stream?
m_ReceiveQueue is a Queue of Byte arrays: Queue<Byte[]>.

Quote:
Do you treat buffer as part of stream?


I don't really understand what you mean by that. Buffer holds the received bytes until they can be copied to an object for processing.
Without order nothing can exist - without chaos nothing can evolve.
Quote:Original post by CyberSlag5k
I don't really understand what you mean by that. Buffer holds the received bytes until they can be copied to an object for processing.


But a single buffer (as used in your example) may contain multiple messages. Do you parse those properly?

If you send 3 messages on server, they may be received in a single call to Read into single buffer.
That can happen with Sockets out of the box? Is that how it reconciles having "missed" a message? So if I send the first message, and before it is done dealing with that, the second two messages come in before it starts listening again, the call to read will pick them up as a single message? If so, how do you deal with that? Do I have to wrap them up in some object that contains its length or something?
Without order nothing can exist - without chaos nothing can evolve.
See Q14 of forum FAQ.
Thank you for your help, Antheus.

I think I've got it. So every packet I send now becomes two, right? So if I have piece of XML that has 512 bytes of data, I would first send an Int16 with a value of 512 as a separate packet, and then I would send my data? If I do that, though, how can I be sure that when the first packet is received, that the second will be there as well? I guess I could check the size of the buffer and if it's greater than 2 bytes then I go ahead and pull off the rest of the data, and if not I wait for the next message and pull off exactly that many bytes?

Or should I bundle the size of the data in with the data itself? So I send 514 bytes as a single packet, pull off the first 2 for the size, and then process the rest? This seems a bit cleaner, and BlockCopy has an offset all ready for me, so I'm thinking that might be the way to do it.

Am I on track here?
Without order nothing can exist - without chaos nothing can evolve.
TCP is a stream protocol, it has no concept of "packets". Think of it like writing a file to disk. You can write the file in chunks of 100 bytes (say) but when you come back later to read the file in, you've got no idea how many times you called "write" or what the size of the "packets" were. If you really need "packet" semantics in TCP (and often you don't) then typically you'd add a fixed-size "header" to your packets which describes things like the "kind" of packet and the size of the packet, etc.

But don't forget, reading from a TCP socket is like reading from a file - you don't know where the "packets" start and where they end.
Yes, prefix each distinct packet with a length (say System.UInt16). Something like this:
byte[] buffer = new byte[16384];int index = 0;int length = 0;while (true){    index += stream.Read(buffer, index, buffer.Length - index);    while (index > 0)    {        if (length == 0)        {            if (index >= sizeof (ushort))            {                length = sizeof (ushort) + //read ushort from buffer            }            else            {                // need more                break;            }        }        if (length != 0 && index >= length)        {            // copy data and use            index -= length;            length = 0;        }    }}
Anthony Umfer
Problem solved. Here's the final version:

if (m_Client.Connected == true){	NetworkStream stream = m_Client.GetStream();	Byte[] buffer = new Byte[100000];	Int32 count = stream.Read(buffer, 0, buffer.Length);	while (count > 0)	{		Int32 remaining = count;		Int32 processed = 0;		while (remaining > 0)		{			Int32 messageSize = buffer[processed] | (buffer[processed + 1] << 8);			Byte[] receivedBytes = new Byte[messageSize];			Buffer.BlockCopy(buffer, 2 + processed, receivedBytes, 0, messageSize);			lock (m_ReceivedQueue)			{				m_ReceivedQueue.Enqueue(receivedBytes);			}			processed += messageSize + 2;			remaining -= processed;		}				count = stream.Read(buffer, 0, buffer.Length);	}}


Thank you to everyone whom helped!
Without order nothing can exist - without chaos nothing can evolve.

This topic is closed to new replies.

Advertisement