Ask Your Question
-1

Send/Receive vector <Mat> over Socket C/C++

asked 2018-08-14 10:57:47 -0500

pwk3 gravatar image

updated 2018-08-23 16:00:43 -0500

Hello, I'm trying to send a vector full of Mat images from one computer to another over a TCP/IP sockets C/C++ connection. Right now I can get the vector of Mat images and send it to another function to save them, then sends .png files over the connection. However, I want to be able to send just the vector over the socket and save the images (with imwrite) on the other system. I can make the connection and send data between them, because I've already sent an array, of type double, over and all the data was correct. When I tried the vector, I get segmentation fault (core dumped) which makes me think it's something to do with the size of the vector being passed incorrectly. I'm pretty new to socket programming, so I'm probably just making simple mistakes. Here is my code so far:

Client ('images' is the vector):

    int sock;
    struct sockaddr_in server;
    #define MB 2591 * 1944

    for(int y = 0; y < EXPECTED_IMAGES; y++)
    {
       //Creates the socket to be used
       sock = socket(AF_INET, SOCK_STREAM, 0);
       if(sock == -1)
       {
           printf("Could not create socket");
       }

       puts("Socket created");

       server.sin_addr.s_addr = inet_addr(IP);
       server.sin_family = AF_INET;
       server.sin_port = htons (port_num);

       //Connects to the remote server
       if(connect(sock ,(struct sockaddr *)&server, sizeof(server)) <0)
       {
            perror("Connect failed. Error");
            return 1;
       }

       if(y == 0)
       {
          send(sock, &expected_images, sizeof(expected_images), 0);
       }

       vector <uchar> buffer;
       buffer.resize(MB);
       imencode(“.jpg”, images[y], buffer);
       int buffer_size = buffer.size();

       send(sock, &buffer_size, sizeof(buffer_size), 0);
       send(sock, buffer.data(), buffer.size(), 0);

       close(sock);
    }

Server:

    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;

    int buffer_size;
    int i = 0;
    int expected_images = 0;
    int num = 5;

    vector <uchar> buffer;
    vector <Mat> images;
    Mat frame;

    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
            printf("Could not create socket");
    }

    puts("Socket created");

    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port_num);

    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
            //print the error message
            perror("bind failed. Error");
            return 1;
    }

    puts("bind done");

    //Listen for incoming clients
    listen(socket_desc , 3);

    puts("Waiting for incoming connections...\n");

    while(i < num)
    {

            int buffer_size = 0;
            int len = 0;
            int remain = 0;

            c = sizeof(struct sockaddr_in);

            //Accept connection from an incoming client
            client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
            if (client_sock < 0)
            {
                    perror("accept failed");
                    return 1;
            }

            if(i == 0)
            {
                    recv(client_sock, &expected_images, sizeof(expected_images), 0);
                    num = expected_images;
            }

            //Recieves the byte size of the client file.
            recv(client_sock, &buffer_size, sizeof(int),0);
            buffer.resize(buffer_size);

            remain = buffer_size;

            while(((len = recv(client_sock, buffer.data(), buffer_size, 0)) > 0) && (remain > 0))
            {

                    //Issue right here for saving the buffer
                    remain -= len;
            }

            //frame = imdecode(buffer, CV_LOAD_IMAGE_UNCHANGED);

            //images.push_back(frame);

            i += 1;

            close(client_sock);

    }

Any help would be appreciated. Thank you.

edit retag flag offensive close merge delete

Comments

no, that will never work. neither std::vector, nor cv::Mat are POD objects. (read up on that, please)

berak gravatar imageberak ( 2018-08-14 10:59:23 -0500 )edit

So Mat data type can't be sent over a socket in any way?

pwk3 gravatar imagepwk3 ( 2018-08-14 11:07:21 -0500 )edit

no, both cv::Mat and std::vector contain pointers to other (layered) data pieces.

berak gravatar imageberak ( 2018-08-14 11:10:51 -0500 )edit

Ok, Thank you.

pwk3 gravatar imagepwk3 ( 2018-08-14 11:12:19 -0500 )edit

it's not "not in any way", though, you just have to do it "smarter". see below.

berak gravatar imageberak ( 2018-08-14 11:26:45 -0500 )edit

1 answer

Sort by » oldest newest most voted
4

answered 2018-08-14 11:19:16 -0500

berak gravatar image

updated 2018-08-24 00:28:00 -0500

that'll be somewhat more complicated, than you expected. you can't simply take the address of it, and send it over the wire, this is c++, not C. what i'd propose is:

server:

  • first send the number of images
  • imencode() each Mat to jpeg (don't ever send uncompressed megabyte pixel images over the wire !).
  • send the encoded bytes (one write() per image). you'll get a vector<uchar> vec, and send(sock, vec.data(), vec.size())

client:

  • read the number of images
  • for each image, have a loop that reads only small packets, like 4k bytes (network fragmentation !). once you recv less than that, you know, that the transmission of an image buffer ended
  • imdecode() the jpeg buffers back to cv::Mat
  • reassemble a vector<Mat>from that
edit flag offensive delete link more

Comments

Thank you for the reply @berak. I'll give it a shot.

pwk3 gravatar imagepwk3 ( 2018-08-14 11:24:51 -0500 )edit
2

my alltime favourite for this.

berak gravatar imageberak ( 2018-08-14 11:34:43 -0500 )edit

For the client, it's handy to know what the maximum transmission unit (MTU) is, assuming that you're using a TCP socket. On Windows it's somewhere in the registry. *nix...?

sjhalayka gravatar imagesjhalayka ( 2018-08-14 20:38:38 -0500 )edit
1

In TCP sockets, there is really no way to get around counting individual bytes. Sockets almost always will return before a whole buffer is written, and you will have to advance buffer pointers (or not) and try to read or write the rest of what you are expecting. This is normal and expected. It's unwise to assume a short read or write means you're done. It could be a network condition anywhere in the stack between client and server, timeout, congestion, or whatever that gives you a short read or write. Usually when you get an error return (<0) for read or write, check errno and respond appropriately.

opalmirror gravatar imageopalmirror ( 2018-08-16 18:05:17 -0500 )edit

Sorry guys, I'm just now getting back to this. I only have one problem that I'm facing. What is the best way for the loop that is receiving the imencoded bytes over the socket to store those bytes into another buffer? I have a while loop that will keep cycling until it receives all the bytes over the socket, this works for a normal file. However, I don't know how to receive and store those bytes into another buffer. Would memcpy work?

pwk3 gravatar imagepwk3 ( 2018-08-22 10:33:16 -0500 )edit

You already know the size of the imencoded data you will receive (BTW you may want to send the size in network byte order for portability - see htonl() library call man page - and convert it back in the receiver - see ntohl() ) . You can dynamically allocate a buffer with char * buf = new char [size]. Either read/recv the bytes directly into this buffer, or memcpy them in, using array pointer offset based on number of bytes received.

opalmirror gravatar imageopalmirror ( 2018-08-22 14:58:40 -0500 )edit

Thanks for reply @opalmirror. Yeah, I get the size of the entire uchar buffer and send it over the socket before I send the actual buffer. However, I'll look into the htonl/ntohl method to see if that works better. If I have to do multiple recv for one image, will memcpy overwrite or get the bytes out of order? I'll update my code that I have currently tomorrow.

pwk3 gravatar imagepwk3 ( 2018-08-22 20:28:09 -0500 )edit

These handle endianness:

  • htonl converts the sending machine's 32-bit int representation to a machine-independent network representation (big-endian).
  • imencode converts the sending machine's Mat representation to a machine-independent representation (JPEG).
  • ntohl converts the machine-independent 32-bit int representation (big-endian) to the local machine's representation.
  • imdecode converts the JPEG machine-independent representation to the local machine's Mat representation.

memcpy is a simple memory to memory copy and performs no conversions.

opalmirror gravatar imageopalmirror ( 2018-08-23 14:14:46 -0500 )edit

I went ahead and updated the code with what I have at the moment. I'll try and implement the htonl/ntohl soon.

pwk3 gravatar imagepwk3 ( 2018-08-23 16:00:09 -0500 )edit
2

With any number of net conditions you will not send/recv full buffers and need to iterate trying to send the rest.

All of your recvs (you can create a function to do this) should be like:

int len;
size_t remain = buffer_size;
size_t offset = 0;
while((remain > 0) && ((len = recv(client_sock, buffer.data() + offset, remain, 0)) > 0))
{
    remain -= len;
    offset += len;
}
if (len <= 0)
{
    // handle fatal error
}

Likewise, sends:

size_t remain = buffer_size;
size_t offset = 0;
int len;
while ((remain > 0) && ((len = send(sock, buffer.data() + offset, remain, 0)) > 0))
{
    remain -= len;
    offset += len;
}
if (len <= 0)
{
    // handle fatal error
}
opalmirror gravatar imageopalmirror ( 2018-08-23 16:39:19 -0500 )edit
Login/Signup to Answer

Question Tools

1 follower

Stats

Asked: 2018-08-14 10:57:47 -0500

Seen: 692 times

Last updated: Aug 24