Here's my code:
// Not all headers are relevant to the code snippet.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
char *buffer;
stringstream readStream;
bool readData = true;
while (readData)
{
cout << "Receiving chunk... ";
// Read a bit at a time, eventually "end" string will be received.
bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE);
if (readResult < 0)
{
THROW_VIMRID_EX("Could not read from socket.");
}
// Concatenate the received data to the existing data.
readStream << buffer;
// Continue reading while end is not found.
readData = readStream.str().find("end;") == string::npos;
cout << "Done (length: " << readStream.str().length() << ")" << endl;
}
It's a little bit of C and C++ as you can tell. The BUFFER_SIZE is 256 - should I just increase the size? If so, what to? Does it matter?
I know that if "end" is not received for what ever reason, this will be an endless loop, which is bad - so if you could suggest a better way, please also do so.
This is an article that I always refer to when working with sockets..
THE WORLD OF SELECT()
It will show you how to reliably use 'select()' and contains some other useful links at the bottom for further info on sockets.
Where are you allocating memory for your
buffer
? The line where you invokebzero
invokes undefined behavior since buffer does not point to any valid region of memory.Without knowing your full application it is hard to say what the best way to approach the problem is, but a common technique is to use a header which starts with a fixed length field, which denotes the length of the rest of your message.
Assume that your header consist only of a 4 byte integer which denotes the length of the rest of your message. Then simply do the following.
Then later in the code
This makes a few assumptions:
Since it is common to want to explicitly know the size of the integer you are sending across the network define them in a header file and use them explicitly such as:
This would change the above to:
I hope this helps.
1) Others (especially dirkgently) have noted that buffer needs to be allocated some memory space. For smallish values of N (say, N <= 4096), you can also allocate it on the stack:
This saves you the worry of ensuring that you
delete[]
the buffer should an exception be thrown.But remember that stacks are finite in size (so are heaps, but stacks are finiter), so you don't want to put too much there.
2) On a -1 return code, you should not simply return immediately (throwing an exception immediately is even more sketchy.) There are certain normal conditions that you need to handle, if your code is to be anything more than a short homework assignment. For example, EAGAIN may be returned in errno if no data is currently available on a non-blocking socket. Have a look at the man page for read(2).
Several pointers:
You need to handle a return value of 0, which tells you that the remote host closed the socket.
For nonblocking sockets, you also need to check an error return value (-1) and make sure that errno isn't EINPROGRESS, which is expected.
You definitely need better error handling - you're potentially leaking the buffer pointed to by 'buffer'. Which, I noticed, you don't allocate anywhere in this code snippet.
Someone else made a good point about how your buffer isn't a null terminated C string if your read() fills the entire buffer. That is indeed a problem, and a serious one.
Your buffer size is a bit small, but should work as long as you don't try to read more than 256 bytes, or whatever you allocate for it.
If you're worried about getting into an infinite loop when the remote host sends you a malformed message (a potential denial of service attack) then you should use select() with a timeout on the socket to check for readability, and only read if data is available, and bail out if select() times out.
Something like this might work for you:
Depending on the volume of data you expect to receive, the way you scan the entire message repeatedly for the "end;" token is very inefficient. This is better done with a state machine (the states being 'e'->'n'->'d'->';') so that you only look at each incoming character once.
And seriously, you should consider finding a library to do all this for you. It's not easy getting it right.
If you actually create the buffer as per dirks suggestion, then:
may completely fill the buffer, possibly overwriting the terminating zero character which you depend on when extracting to a stringstream. You need: