2

i'm trying to develop a little web server in C++ but i have a problem when i try to read an image file and write it in a socket buffer. I found a similar function written in C that works perfectly, i cannot understand why my method doesn't work, when i connect to the server by browser and open an image file i got this output.

"The image "http://127.0.0.1:7777/myimage.jpg" cannot be displayed because it contains errors."

This is my method:

std::string
Client::getFileContent(const std::string& name)
{
    std::ifstream f;     f.open(name.c_str(), std::ios::binary);
    if( !f.is_open() )  {
        return (std::string(""));

    }  else  { 
        f.seekg(0, std::ios::end);
        unsigned int length = f.tellg(); 
        f.seekg(0, std::ios::beg);

        char* buffer = new char[length];    
        f.read(buffer, length);            

        f.close();

        return ( std::string(buffer) );
    }

}

And then i write it in a socket buffer(use nspr socket):

void
Socket::send(const std::string& s)
{
    if(PR_Send(sock, s.c_str(), s.length(), 0, PR_INTERVAL_NO_WAIT) == -1)  {
        throw ( Exception::Exception(Exception::Exception::SOCKET_SEND) );
    }
}

And this is the function i found on web, i cannot understand why this works perfectly and mine doesn't work O.o:

    while ( (ret = read(file_fd, buffer, BUFSIZE)) > 0 ) {
        (void)write(fd,buffer,ret);

Thank you very much :)

nmichaels
  • 49,466
  • 12
  • 107
  • 135
alkz
  • 337
  • 2
  • 7
  • 17
  • You should use [`sendfile(2)`](http://linux.die.net/man/2/sendfile) for sending a file over a socket instead -- it's much more efficient because it doesn't require coping all of the file data back and forth between the kernel and userspace. – Adam Rosenfield Dec 05 '11 at 19:39

1 Answers1

5

You problem is here:

return ( std::string(buffer) );

Creating a string from a char * will stop at the first 0 byte, while an image may contain a lot. instead of returning a string, return a vector<char> like

return std::vector<char>(buffer, buffer + length);
Daniel
  • 30,896
  • 18
  • 85
  • 139
  • 1
    `std::string` can contain null bytes no problem, but you have to use a different constructor. Constructing `std::string` from a `char*` will stop on a null byte. But you can very well return `std::string(buffer, buffer + length)`. –  Dec 05 '11 at 19:39
  • `std::vector` is better however because you can read directly into it. It has always worked, and C++03 explicitly allows it: `std::vector result(length); f.read(result.data(), length); return result;` - saves you the `new char[]` which just leaks in the original code. It is not possible to read into `std::string` in the same way. –  Dec 05 '11 at 19:44
  • @JulianRaschke: actually its possible to read into `std::string`. just use `&string[0]` instead of `data()` – Daniel Dec 05 '11 at 22:17
  • `std::basic_string::data` returns a const pointer because you *cannot* use it for writing. A standards conforming C++ compiler could implement copy-on-write and only ensure a string's pointer is unique once you actually call `data`, for example. Or it could use non-contiguous memory. –  Dec 05 '11 at 22:27
  • @JulianRaschke: it returns non-const reference in its subscript operator, so it must expect writing to it. – Daniel Dec 05 '11 at 22:31
  • Writing to a single character, yes. The C++03 standard does not guarantee a contiguous memory layout. I am not sure about C++0x though; if it does, then it is new. And AFAIK there are real-world implementations that use copy-on-write, so writing to a `std::string` in the general case would still be unsafe. `std::vector` is much clearer. –  Dec 05 '11 at 22:32
  • 1
    @JulianRaschke: there is no sane implementation that doesn't use a contiguous storage, especially when you initialize the string with the final size. – Daniel Dec 05 '11 at 22:35