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 ?
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.
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
}
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.
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)
.
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.