Skip to content
Advertisement

How to send image data over linux socket

I have a relatively simple web server I have written in C++. It works fine for serving text/html pages, but the way it is written it seems unable to send binary data and I really need to be able to send images.

I have been searching and searching but can’t find an answer specific to this question which is written in real C++ (fstream as opposed to using file pointers etc.) and whilst this kind of thing is necessarily low level and may well require handling bytes in a C style array I would like the the code to be as C++ as possible.

I have tried a few methods, this is what I currently have:

int sendFile(const Server* serv, const ssocks::Response& response, int fd)
{
// some other stuff to do with headers etc. ........ then:

    // open file
    std::ifstream fileHandle;
    fileHandle.open(serv->mBase + WWW_D + resource.c_str(), std::ios::binary);

    if(!fileHandle.is_open())
    {            
        // error handling code
        return -1;
    }

    // send file
    ssize_t buffer_size = 2048;
    char buffer[buffer_size];

    while(!fileHandle.eof())
    {
        fileHandle.read(buffer, buffer_size);

        status = serv->mSock.doSend(buffer, fd);
        if (status == -1)
        {
            std::cerr << "Error: socket error, sending filen";
            return -1;
        }
    }
    return 0
}

And then elsewhere:

int TcpSocket::doSend(const char* message, int fd) const
{
    if (fd == 0)
    {
         fd = mFiledes;
    }

    ssize_t bytesSent = send(fd, message, strlen(message), 0);
    if (bytesSent < 1)
    {
        return -1;
    }

    return 0;
}

As I say, the problem is that when the client requests an image it won’t work. I get in std::cerr “Error: socket error sending file”

EDIT : I got it working using the advice in the answer I accepted. For completeness and to help those finding this post I am also posting the final working code.

For sending I decided to use a std::vector rather than a char array. Primarily because I feel it is a more C++ approach and it makes it clear that the data is not a string. This is probably not necessary but a matter of taste. I then counted the bytes read for the stream and passed that over to the send function like this:

// send file
std::vector<char> buffer(SEND_BUFFER);
while(!fileHandle.eof())
{
    fileHandle.read(&buffer[0], SEND_BUFFER);
    status = serv->mSock.doSend(&buffer[0], fd, fileHandle.gcount());
    if (status == -1)
    {
        std::cerr << "Error: socket error, sending filen";
        return -1;
    }
}

Then the actual send function was adapted like this:

int TcpSocket::doSend(const char* message, int fd, size_t size) const
{
    if (fd == 0)
    {
        fd = mFiledes;
    }

    ssize_t bytesSent = send(fd, message, size, 0);
    if (bytesSent < 1)
    {
        return -1;
    }

    return 0;
}

Advertisement

Answer

The first thing you should change is the while (!fileHandle.eof()) loop, because that will not work as you expect it to, in fact it will iterate once too many because the eof flag isn’t set until after you try to read from beyond the end of the file. Instead do e.g. while (fileHandle.read(...)).

The second thing you should do is to check how many bytes was actually read from the file, and only send that amount of bytes.

Lastly, you read binary data, not text, so you can’t use strlen on the data you read from the file.


A little explanations of the binary file problem: As you should hopefully know, C-style strings (the ones you use strlen to get the length of) are terminated by a zero character '' (in short, a zero byte). Random binary data can contain lots of zero bytes anywhere inside it, and it’s a valid byte and doesn’t have any special meaning.

When you use strlen to get the length of binary data there are two possible problems:

  1. There’s a zero byte in the middle of the data. This will cause strlen to terminate early and return the wrong length.

  2. There’s no zero byte in the data. That will cause strlen to go beyond the end of the buffer to look for the zero byte, leading to undefined behavior.

Advertisement