12

So far, I have this code sample:

...
int nbytes =0;
vector<unsigned char> buffer;
buffer.resize(5000);
nbytes = recv(socket, &buffer[0], buffer.size(),0);
//since I want to use buffer.size() to know data length in buffer I do
...
buffer.resize(nbytes);

Is it some another way, to know data length in buffer without using resize() twice? Because it is not possible to receive data into vector that is not resized to proper size. I think reserve() method don't do allocation, according to the C++ STL documentation. And another question: is using this kind of technique is memory leak-safe ?

Hitman_99
  • 2,252
  • 2
  • 19
  • 21
  • 2
    Also, if it helps you, you can pass MSG_PEEK to the flags parameter of recv() to find out how many bytes are actually there. – Moo-Juice Nov 19 '10 at 09:43
  • Actually, there is not much use in resizing your received message. Just unmarshall it, handle it, reply to it and wait for the next. Or are you storing your binary requests? – stefaanv Nov 19 '10 at 10:10
  • 1
    I resize buffer after receiving the messages because it is handy to use buffer.size() instead of some other variable, that holds the actual buffer length, in other functions to know how big is the message. – Hitman_99 Nov 19 '10 at 10:14
  • Are you using UDP or TCP (for TCP, nbytes doesn't give the size of the sent message)? – stefaanv Nov 19 '10 at 10:36
  • recv() is for TCP sockets and yes, nbytes gives the size of the message received, without headers and etc. – Hitman_99 Nov 19 '10 at 13:18

5 Answers5

5

There is not much you can do, you cannot know the post size before the recv call.

Some cleanup:

std::vector<unsigned char> buffer(5000);
int result = recv(socket, buffer.data(), buffer.size(), 0);
if (result != -1) {
   buffer.resize(result);
} else {
   // Handle error
}
Community
  • 1
  • 1
ronag
  • 49,529
  • 25
  • 126
  • 221
  • Actually, doing that is dangerous, because recv() can return -1, if and error has occurred – Hitman_99 Nov 19 '10 at 09:49
  • Noted, changed the code. The author did not include that check, which is why i mistakenly ignored this possibility. – ronag Nov 19 '10 at 09:51
  • I check for the result, just forgot to add "..." above the buffer.resize(nbytes). Changed. – Hitman_99 Nov 19 '10 at 09:59
3

This technique is leak-safe, quite clean and preferable. Using std::vector is the recommended way of implementing a variable-length buffer in C++.

If you find that not all data fits into the vector - no problem, just resize it to bigger size and pass the address of the section that follows the already-filled part.

Using reserve() is not a very good idea - it doesn't affect what size() returns, so you will lose convenience and likely will gain no advantages.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Arguably (of course), using `reserve` before the read and `resize` afterwards communicates knowledge about known state more accurately, (but ronag's rewrite cleans that up anyway...) – Tony Delroy Nov 19 '10 at 09:57
  • @Tony: Well, maybe, but since you usually can't know the total size of the message any attempt to predict the size can fail and you'll have to reallocate anyway. – sharptooth Nov 19 '10 at 10:00
  • depends what you're doing with it... you may just want to write up to 5k out to some other stream, then try to get another 5k. You may know the stream contains discrete messages of up to N bytes where N is less than 5K, so you process all the complete ones, copy the remainder down to [0], and keep reading.... – Tony Delroy Nov 19 '10 at 10:06
  • vector::resize() initializes the buffer with default values, which is unneccssary and a waste of time in this case. It's like calling calloc() when malloc() is enough. – zeodtr Oct 06 '14 at 05:41
1

This code is fine. The difference between resize and reserve is, that resize changes the value returned by size (and actually creates new default initialized objects), whereas reserve does not (it only allocates more memory).

Depending on how you process the data, you can leave the second resize out, and do it with a loop like this:

for (vector<unsigned char>::iterator it = buffer.begin(); 
     it != buffer.begin() + nbytes; 
     it++) 
{
    // process each byte
}

Thus you can just read the data that was actually written, and ignore the rest. This means you would only set the size of the vector once, and then never change it. In general, as long you only work with iterators, there is no need to resize the vector, as the valid data range will always be [buffer.begin(), buffer.begin() + nbytes).

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
1

I don't believe so [a more elegant way?]. Fundamentally, you need to have more than enough characters in the buffer to recv many bytes; then once you've read them, if you want the buffer to only contain the received bytes you need to resize downwards. What you've shown is probably similar to how I would approach things.

You are correct that reserve is not sufficient. You cannot write to elements that don't exist and have only had storage allocated for them in advance.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
0

There is a more elegant way when using TCP: use a message header.
You know the (fixed) size of the header, so you can read this in a fixed size buffer.
Inside the header, there is a message size, so you can use that to allocate your buffer and read the number of bytes as specified in the header.

Mind you that using this, you might need some techniques for secial cases like fragmenting for long messages.

stefaanv
  • 14,072
  • 2
  • 31
  • 53