Reputation: 438
So I'm streaming my Rasperry Pi camera to my computer using my program but. The vector listed below is giving me problems. It gives me std::bad_alloc
after about 30 seconds of streaming. Is there any way to reuse this vector over and over again in a loop (e.g resize, clear)?
Here is the simplified code:
while(isRunning)
{
recv(Connection, received_message, sizeof(received_message), NULL); //receiving the size of image in bytes
fileSize = atoi(received_message);
std::vector<char> fileData(fileSize); //<- this vector is giving me problems
recv(Connection, &fileData[0], CHUNK_SIZE, 0); //Receiving the image
//The code loops over and over again
}
Upvotes: 2
Views: 145
Reputation: 33952
TCP is a streaming protocol. It has no concept of messages. Writing 10,000 bytes in at one end of the connection does not mean all 10,000 bytes will arrive at the receiver and be available all at once.
As a result, recv
works with what is has. It returns whatever is currently available, and if nothing is available recv
waits until data becomes available. This means if you ask for 10,000 bytes you may get anywhere between 1 byte and 10,000 bytes entirely at the whim of the network stack, the maximum amount of data that can go in one IP packet, and too many other variables to list.
So
recv(Connection, received_message, sizeof(received_message), NULL);
may return before it receives all of received_message
. fileSize
will computed from bad input, most likely a string that isn't null terminated and runs off the end of the buffer triggering undefined behaviour, and garbage in gives garbage out.
This incorrect fileSize
is then used to size a vector
which will now almost certainly be the wrong size. If it is too small,
recv(Connection, &fileData[0], CHUNK_SIZE, 0);
may run off the end of the vector
for more undefined behaviour. If it is too large, the system may not be able to allocate storage for the vector
because there isn't enough contiguous storage available. This appears to be what has happened to OP.
Solution: Loop all calls to recv
until the required amount of data has arrived before proceeding. Write in an alternate path to handle closed or failed connections. All calls must read the correct amount of data or
recv(Connection, &fileData[0], CHUNK_SIZE, 0);
could exit early leaving the next
recv(Connection, received_message, sizeof(received_message), NULL);
to read part of the image as received_message
, resulting in a fileSize
every bit as insane as if received_message
was not completely filled.
Also Consider setting a timeout on recv
so that you have a chance to read an exit flag should you wish to terminate the program. Otherwise it may block forever for data that will never arrive.
Upvotes: 2
Reputation: 7017
You can easily reuse your std::vector
like this:
std::vector<char> fileData;
while(isRunning)
{
ssize_t n = recv(Connection, received_message, sizeof(received_message), 0); //receiving the size of image in bytes
if (n < 0)
throw std::runtime_error(std::string("Connection error (getting fileSize)") + strerror(errno));
assert(n == sizeof(received_message));
fileSize = atoi(received_message);
if (fileSize > maxFileSize or fileSize == 0)
throw std::runtime_error("Invalid fileSize " + std::to_string(fileSize));
fileData.resize(fileSize);
size_t received = 0;
while (received < fileSize)
{
ssize_t n = recv(Connection, fileData.data() + received, fileSize - received, 0); //Receiving the image
if (n < 0)
throw std::runtime_error(std::string("Connection error (getting image)") + strerror(errno));
received += n;
}
//The code loops over and over again
}
A few notes:
recv
does not receive sizeof(received_message)
bytes (currently protected by the assert
)maxFileSize
<cassert>
, <exception>
and <string>
headers-std=c++11
Upvotes: 0