0

The following code iterates through a List<byte[]>() however the problem is that for some reason, sometimes the lengthBuffer is a different size of the byteData.

You can see in the DebugPrintInGame method that I print the BitConverter.ToInt32() length and then the actual byteDataList[i].Length, these two numbers should be exactly the same and a lot of the time they are, but for reasons unknown to me a lot of the time now they aren't either.

With this code below it sends the length of the data to the server so server knows size of incoming data and what to expect, then it sends the actual data. However sometimes for example it will send the length as larger than the actual data sent but it happens randomly, which led me to believe maybe it is a threading issue - nonetheless byteDataList is only used within this one thread so its pointless for me to even lock it. In what I've tested, the data I'm sending is no more than 2kb to 17kb.

Does anyone see why this might be happening based on this little snipit from my project? This code is all client side. Notice BitConverter.ToInt32(lengthBuffer, 0) and byteDataList[i].Length is expected to give same length but it does not. That is the problem.

List<byte[]> byteDataList = new List<byte[]>();
//.........

lock (byteDataList) //pointless lock, just used for random testing :\
{
      if (byteDataList.Count > 0)
      {
             for (int i = 0; i < byteDataList.Count; i++)
             {
                 try
                 {
                      byte[] lengthBuffer = BitConverter.GetBytes(byteDataList[i].Length);
                      //quick printout to compare lengths for testing
                      this.QueueOnMainThread(() =>
                      {
                        Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                               +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                               + byteDataList[i].Length);  //this should be same as BitConverter lengthBuffer
                      });
                      //send length
                      stream.Write(lengthBuffer, 0, lengthBuffer.Length);
                      //send data
                      stream.Write(byteDataList[i], 0, byteDataList[i].Length);
                      stream.Flush();
                  }
                  catch (Exception ex)
                  {
                       this.QueueOnMainThread(() =>
                       {
                           Tools.DebugPrintInGame("Exception " + ex.ToString());
                       });
                  }
             }

      }
}

Print out from client side before it's even sent to server:

Notice different lengths?

Notice different lengths when the last two should be the same. I.e.

 `lengthBuffer`    = 17672 
 `byteDataList[i]` = 17672 

but sometimes it goes weird and wonky:

 `lengthBuffer`    = 17672
 `byteDataList[i]` = 2126

Both lengthBuffer and byteDataList[i] should be the same.

Euthyphro
  • 700
  • 1
  • 9
  • 26
  • Yes, `byte[] Array.Length` will give you the size in bytes of a byte array. – Euthyphro Jan 14 '15 at 21:15
  • Yea and then I'm sending that array element. Length of the element is sent, then that actual element is sent. Look at the code, notice [i]? – Euthyphro Jan 14 '15 at 21:20
  • For debugging purposes I think you should add the length in bytes to the start of the data array since its a representation of an int it will always be the same length so you can just read and check them at the other end to make sure its not a a latency/network issue. Or do you see the peoblem in your printouts too? – kmcc049 Jan 14 '15 at 21:35
  • The image in the post is from client side before it's even sent to the server. – Euthyphro Jan 14 '15 at 21:36
  • This whole post is clientSide code. lengthBuffer and byteDataList[i] is printing out on the client. See this code, it is all client side. The problem is that it is giving the wrong length before it's even sent to the server for reasons unknown to me, which you can see in the print out. – Euthyphro Jan 14 '15 at 21:39
  • I agree with Dan Bryant's assessment below. See Jon Skeet's answer [here](http://stackoverflow.com/a/271447/2330053) for another example. – Idle_Mind Jan 14 '15 at 21:45

1 Answers1

1

This is most likely a problem with closure over the i variable in your loop (I assume you're using .NET 4.0 or earlier?) The culprit is here:

this.QueueOnMainThread(() =>
                      {
                        Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                               +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                               + byteDataList[i].Length);  //this should be same as BitConverter lengthBuffer
                      });

See how you reference the variable i above? The above anonymous method forms a closure over that variable, which means i is not evaluated until the method is called. This means that the value of i may no longer be the one that you used to create lengthBuffer, so you get a mismatch.

To fix this, store a copy of i into a variable in the inner scope of the loop, like this:

int k = i;
this.QueueOnMainThread(() =>
                          {
                            Tools.DebugPrintInGame("Length buffer " + lengthBuffer.Length + 
                                                   +  BitConverter.ToInt32(lengthBuffer,0)+ " "+
                                                   + byteDataList[k].Length);  //this should be same as BitConverter lengthBuffer
                          });

By closing over an inner scope variable, it will have the intended value and won't change as the loop executes. The confusion here is that a loop variable is actually considered to be out of the scope of the loop body for the purpose of closure. In later versions of C# (for .NET 4.5), they changed this behavior and the loop variable is now closed over with inner scope. They made the change because it was too easy to trip over this problem in exactly this type of situation.

Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
  • Yes using .net 2.0, thanks! Now onto why server is receiving less data than expected. – Euthyphro Jan 14 '15 at 21:52
  • @Euthyphro, do you wait long enough for all the TCP packets to arrive? A single receive may not get all the data if you cross a packet boundary, so it's necessary to buffer and receive data until you have a complete message. In cases where you explicitly know the size, though, most TCP APIs give you a way to block until exactly N bytes have been received. – Dan Bryant Jan 14 '15 at 21:57
  • On the server I'm not stuck with .net 2.0 so I use `var initialBuffer = new byte[4]; var lengthBuffer = await receiveStream.ReadAsync(initialBuffer, 0,4); requestLength = BitConverter.ToInt32(initialBuffer, 0);` to get the length of incoming data and then after `var bytesRead = await receiveStream.ReadAsync( requestBuffer,0,requestLength);` to receive the actual data based on expected length. It is my understanding this should block and wait. If size is not the same I have it throw an exception. – Euthyphro Jan 14 '15 at 22:04
  • 1
    @Euthyphro, ReadAsync will 'block' if there is no data present at all, but will not block if there are fewer bytes than the count specified. You may need to do your own reading/buffering until the entire buffer has been read. – Dan Bryant Jan 14 '15 at 22:10