1
votes

I am trying to send image frames over the network using Winsock. I know array length and their dimensions, so I'm just using a char buffer of sufficient constant size. The following conversion works fine and in real time:

char buffer[BUFFER_SIZE]; //BUFFER_SIZE = 1000000
...
UINT16 *pBuffer = NULL; //send buffer
UINT16 *pBuffer2 = NULL; //receive buffer
...
//copying to the buffer of correct length to avoid errors. 
//Empirically found that it seems contiguous
memcpy(&buffer[1], pBuffer, nBufferSize*2);//img is int16, nBufferSize*2 ~ 400000
buffer[0] = '~'; //adding special symbol to mark correct data stream

// here supposed to be send-receive with buffer

//convert received result back to UINT16
pBuffer2 = (UINT16*)&buffer[1];

Here is how correct image from pBuffer2 looks like: good depth image

Now I'm sending this char buffer over the network:

client_send(buffer);

//receiving frame from server   

receive_data(buffer);

Where the functions look like the following:

Client part(send):

#define DEFAULT_BUFLEN 1000000 //1MB
...
int client_send(char *sendbuf) {

    // Send an initial buffer

        //iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
        iResult = send(ConnectSocket, sendbuf, DEFAULT_BUFLEN, 0);
        if (iResult == SOCKET_ERROR) {
            printf("send failed with error: %d\n", WSAGetLastError());
            closesocket(ConnectSocket);
            WSACleanup();
            return 1;
        }

        printf("Bytes Sent: %ld\n", iResult);       

    return 0;
}

Server part(receive) - I truncated the code to one client at the moment:

const int BUFFER_SIZE = 1000000;//1MB
...
int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {
        // Store the return data of what we have sent
        current_client->address_length = recv(current_client->socket, buffer, size, 0);

        if (current_client->address_length == 0)
        { // Data error on client
            disconnect_client(current_client);

            return (FALSE);
        }

        return (TRUE);
    }

    return (FALSE);
}

int receive_data(char* buffer)
{
    //char buffer[BUFFER_SIZE];

    for (int j = 0; j < MAX_CLIENTS; j++)
    {
        if (client[j].connected)
        {
            if (receive_client(&client[j], buffer, BUFFER_SIZE))
            {
                if (buffer[0] == '~') {
                    // if we have correct packet                    
                    return 1;
                    //buffer[0] = '/0';
                } 
            }
        }
    }

    return 0;
}

The result becomes randomly messed up like frame was randomly shifted in XY plane, and the shift is constant over time (though different for different sessions):

depth_ugly

I have ensured that correct stream begins with ~ symbol and I have no idea on the nature of that shift.

1
I might be mistaken (just can't see what I am looking for in the code). Your sent image buffer will get split into multiple packets and need to be reassembled in the client. Also multiple sent images will get joined to each other, there is no requirement for a packet boundary to be at an image boundary. - Richard Critten
@RichardCritten Thank you! I'm just using send/recv in the code above with char array of constant size and it worked fine for text messages, what I need to do to reassemble image chunks correctly? - Slowpoke
One way is a message format eg [length-of-image][image]. Where the 1st 4 bytes are how long the image data is followed by the the image data. You loop on the receiving side reassembling the image until [length of image] bytes have been added to the image buffer. You can extend this with [message-type][message-length][message] for more generalised exchanges. - Richard Critten
@RichardCritten As far as I understand, I have the same setup you described: I know the image size nBufferSize*2 (after conversion to uint16) on both sides and I am receiving a message with single buffer of size 1000000(which is bigger than image written into it) in recv function, having ~ at the beginning which is supposed to be followed by image data. Then I access this buffer data by pointer that points to the first element. What do you mean by reassembling it? Do you mean that I need to receive image in one byte per packet? - Slowpoke
He means that you are not guaranteed to receive your entire buffer with a single read. I assume your image size is fixed so that you don't need to encode that data in your transmission, but it would not hurt to do so for the general case. - Mikel F

1 Answers

0
votes

So the solution is:

There is no need to send/receive in extra small chunks, you may use 1MB per packet if bandwidth allows to do it. The problem is that length of packets send/received may differ at first (wait a second, I will explain what is buffer2):

SOCKET ESTABLISHED
Binding socket:0
Socket Bound to port : 4000
ACCEPTING CLIENT to array position [0] with IP ADDRESS 127.0.0.1
buffer received: -1, 
Bytes Sent: 1000000
buffer received: 992800, 
got beg. packet
buffer2 received: 7200, 
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 
Bytes Sent: 1000000
Bytes Sent: 1000000
buffer received: 1000000, 
got beg. packet
buffer2 received: 0, 

So the solution is to add a reliable opening sequence at the beginning of the image: [strbegin][...image data..] and receive 1MB packets, searching for the opening sequence there:

/* BUFFER_SIZE = 1000000 */

int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {
         char smallbuffer[BUFFER_SIZE]; //secondary incoming buffer
         current_client->address_length = recv(current_client->socket, smallbuffer, BUFFER_SIZE, 0); //recv returns the number of bytes received 

         if (current_client->address_length == 0)
            { // Data error on client
                disconnect_client(current_client);

                return (FALSE);
            }

            printf("buffer received: %d, \n", current_client->address_length);

            char * pch = strstr(smallbuffer, strbegin); //searching for the opening sequence

When you encountered it, you need to copy the rest of the smallbuffer into the resulting variable and read next packet. But be careful, as the packet received may be shorter than 1MB! So you need to take the number of bytes received into account (the data is not lost, it just arrives with the next TCP chunk):

if (pch != nullptr) { // if we encountered the beginning in some way

    printf("got beg. packet\n");

    int position = pch - smallbuffer; // pointer subtraction, http://stackoverflow.com/questions/7500892/get-index-of-substring

    int substringLength = (current_client->address_length) - position;

    memcpy(&buffer[0], pch, substringLength); // copy the rest of the data into buffer

    current_client->address_length = recv(current_client->socket, &buffer[substringLength], size-substringLength, 0); //read the rest of the message 

    printf("buffer2 received: %d, \n", current_client->address_length);

}

You can see above that this code synchronized with data arrival after few steps, reading entire 1MB message in first pass. Also some frame drop is possible in this code, but my live transfer does not demand precision.

By the way, my server is not blocking, otherwise program hangs:

// This makes the server non blocking, hence it won't wait for a response
    unsigned long b = 1;
    ioctlsocket(server_socket, FIONBIO, &b);

EDIT: Well, after I switched from localhost to real LAN network, I found that real size of messages transferred is about 40KB. So the receiving side needs modification to be able to reassemble the data from a big number of chunks (notice that I'm not breaking the loop when no data was read, as server update works faster now than data arrives):

int receive_client(_client *current_client, char *buffer, int size)
{
    if (FD_ISSET(current_client->socket, &current_client->socket_data))
    {

        //attempt first read into secondary buffer
            current_client->address_length = recv(current_client->socket, smallbuffer, BUFFER_SIZE, 0); //recv returns the number of bytes received 

            if (current_client->address_length == 0)
            { // Data error on client
                disconnect_client(current_client);

                return (FALSE);
            }

            printf("buffer received: %d, \n", current_client->address_length);

            char * pch = strstr(smallbuffer, strbegin); //searching for the opening sequence

            if (pch != nullptr && (current_client->address_length > 0)) { // if we encountered the beginning in some way

                printf("got beg. packet\n");

                int position = pch - smallbuffer; // pointer subtraction, http://stackoverflow.com/questions/7500892/get-index-of-substring

                int substringLength = (current_client->address_length) - position;

                memcpy(&buffer[0], pch, substringLength); // copy the rest of the data into buffer

                int tmpsize = size - substringLength;   // how many we still need to read

                int count = 1;
                int where_we_are = substringLength;
                do  // repeat until buffer is filled
                {
                    current_client->address_length = recv(current_client->socket, smallbuffer, tmpsize, 0); //read the next chunk
                    substringLength = current_client->address_length; // how much we read this time

                    if (substringLength > 0){
                        memcpy(&buffer[where_we_are], smallbuffer, substringLength); //add it to buffer

                        where_we_are += substringLength;
                        tmpsize = tmpsize - substringLength;
                        count++;
                    }
                    else {
                        //tmpsize = 0; // nope, can't break - need to read until the end ! Seems like reading is performed faster than data arrival
                    }
                } while (tmpsize > 0);

                printf("buffer2 received: %d chunks \n", count);


            }


        return (TRUE);
    }

How it looks like from inside:

buffer received: -1, 
buffer received: 182500, 
got beg. packet
buffer2 received: 356 chunks 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: -1, 
buffer received: 589840, 
got beg. packet
buffer2 received: 238 chunks 
buffer received: -1, 
buffer received: 589940, 
got beg. packet
buffer2 received: 168 chunks 
buffer received: -1, 
buffer received: 562936, 
got beg. packet
buffer2 received: 227 chunks 
buffer received: -1, 
buffer received: 590140, 
got beg. packet
buffer2 received: 204 chunks 
buffer received: -1, 
buffer received: 590240,