3

I am new to tcp, and have set up two applications, one in c++ that sends data, and one in c# that receives it. I send two float arrays, each containing three floats.

The data transfers, and unpacks fine, but the order of the data does not stay consistent. For example, I send float1, float2, float3, and receive float2, float1, float3.

My applicable c++ is:

float position[3];
float rotation[3];

for (int i = 0; i < 3; i++)
        {
            iResult = send(ConnectSocket, (char*)&position[i], (int) sizeof(float),4);
            iResult = send(ConnectSocket, (char*)&rotation[i], (int) sizeof(float),4);
        }
        if (iResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            //return 1;
        }

and c#:

clientSocket = serverSocket.Accept();
Console.WriteLine("Server: Accept() is OK...");
Console.WriteLine("Server: Accepted connection from: {0}", clientSocket.RemoteEndPoint.ToString());

                            // Receive the request from the client in a loop until the client shuts
                            //    the connection down via a Shutdown.
                            Console.WriteLine("Server: Preparing to receive using Receive()...");
                            while (true)
                            {
                                rc = clientSocket.Receive(receiveBuffer);
                                float transX = System.BitConverter.ToSingle(receiveBuffer, 0);
                                float transY = System.BitConverter.ToSingle(receiveBuffer, 4);
                                float transZ = System.BitConverter.ToSingle(receiveBuffer, 8);

                                float rotX = System.BitConverter.ToSingle(receiveBuffer, 12);
                                float rotY = System.BitConverter.ToSingle(receiveBuffer, 16);
                                float rotZ = System.BitConverter.ToSingle(receiveBuffer, 20);


                                Console.WriteLine(transX + " " + transY + " " + transZ + "\n");
                                Console.WriteLine(rotX + " " + rotY + " " + rotZ + "\n");
                               // Console.WriteLine("Server: Read {0} bytes", rc);
                                if (rc == 0)
                                    break;
                            }

What is my error here? Should I be sending the floats individually? Thanks.

Christophe
  • 68,716
  • 7
  • 72
  • 138
anti
  • 3,011
  • 7
  • 36
  • 86

2 Answers2

4

The first error is to assume that you'll receive the data exactly as you've sent it.

Unfortunately, when sending several floats, the receiver may receive them all together (as you expect), but it could as well receive them in several pieces (down to byte by byte in an hypothetical worst case).

So in you while loop you may for example receive 10 bytes only but you suppose to have received 24. Leading to a lot of garbage.

Solution: check with rc that you've received the right amount of data before assuming it's there, and implement the loop in a way to properly manage buffer (i.e. the end of receiving of the first group of data, might come together with the begin of the next sequence).

There's a second more subtle issue: your code logic works only if the source and the target use the same floating point encoding. The C++ standard doesn't fix this encoding (it's frequently IEEE-754 but it doesn't have to be, and even if it is, different endianness could affect the order of the bytes send over the network).

This may not be a problem if you target only with wintel platform. But I think it's worth to mention in case your C++ part is for an IoT device using a different architecture ;-)

Edit: hint for processing buffered reception

I'm not fluent in C#, but the principle would be something like:

    Console.WriteLine("Server: Preparing to receive using Receive()...");
    rc=1; 
    br=0;  // number of bytes in buffer  
    sf=24; // size of one sequence to be received
    while (true)
    {
        while (br<sf && rc>0) { // fill buffer until all bytes of one sequence are there
            rc = clientSocket.Receive(receiveBuffer, br, sf-br,SocketFlags.None);
            br += rc; 
        }             
        if (rc == 0) {
            if (br>0) 
                  Console.WriteLine("Server: interupted while receiving data...");
               break;
        }
        //... here you can convert the content of the buffer 
    }
Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • thank you! So i can add 'if (rc == 24)'... What would recommend to manage the buffer? Can I add some kind of end marker/character ? – anti Mar 12 '16 at 14:15
  • 1
    Whether or not you use a separator will not fundamentally change the way you'll loop. You need to keep track in the buffer of the position where to put the next chunk of data to be received. As soon as the length of data in buffer is 24 OR MORE, you can perform your conversion. Then you have to move the remainder of the buffer at the beginning, so to be able to complete the next chunk of data (that's because of the possible overlap with the next sequence of floats). – Christophe Mar 12 '16 at 14:28
  • Sorry I thought I had this, but am still getting the value switching. Are you able to point me in the right direction to move the remainder of the data to the beginning? Thank you again! – anti Mar 12 '16 at 15:28
  • I'm not so fluent in C#, but `Array.Copy()` as explained [here](http://stackoverflow.com/a/2381353/3723423), should do the trick. – Christophe Mar 12 '16 at 16:22
  • Cool, ok. Sorry to be annoying, but could you run me through the logic? Something like: If (rc == 24 || rc > 24) {//perform conversion}, but then how do I know which part of the buffer to move, and to where? – anti Mar 12 '16 at 16:43
  • in this code, rc always= 24. So I am confused as to how it gets out of order like that! – anti Mar 12 '16 at 17:10
  • 1
    I've edited, and I suggest to use of [receive with additional arguments](https://msdn.microsoft.com/en-us/library/w3xtz6a5%28v=vs.110%29.aspx). By limiting the max size argument to 24, it simplifies things avoiding moving parts of the buffer. – Christophe Mar 12 '16 at 17:16
  • Thank you, that makes sense. I have added a reset: br = 0; at the end of the loop. Works great. Thank you once again! – anti Mar 12 '16 at 17:32
3

The order you're sending your floats in is like so:

pos1
rot1
pos2
rot2
pos3
rot3

However, when receiving them, you're processing the data like this:

pos1
pos2
pos3
rot1
rot2
rot3

The solution here would be to read the floats from the right indices.

float transX = System.BitConverter.ToSingle(receiveBuffer, 0);
float transY = System.BitConverter.ToSingle(receiveBuffer, 8);
float transZ = System.BitConverter.ToSingle(receiveBuffer, 16);

float rotX = System.BitConverter.ToSingle(receiveBuffer, 4);
float rotY = System.BitConverter.ToSingle(receiveBuffer, 12);
float rotZ = System.BitConverter.ToSingle(receiveBuffer, 20);
cbr
  • 12,563
  • 3
  • 38
  • 63
  • ah, thank you! This(obviously) has put them in the correct order. However, they do not stay in the same order. when I print the above in a loop, i see pos1, pos2, pos3, then the next loop will give pos2, pos1, pos3, and so on. – anti Mar 12 '16 at 14:02