spe
spe

Reputation: 1041

boost::asio file sending

There is a server that response a .png file over http:

#include "server.h"
string Server::header(int contentLength)
{
    string h =
    "HTTP/1.1 200 OK\n"
    "Content-Length: " + boost::lexical_cast<string>(contentLength) + "\n"
    "Content-Type: image/png;\n"
    "Connection: close\n"
    "\n";
    return h;
}
string Server::readMap(const string &filename)
{
    ifstream file (filename.c_str(), ios::in|ios::binary);
    string reply;
    char buf[512];
    while (file.read(buf, sizeof(buf)).gcount() > 0)
        reply.append(buf, file.gcount());
    return reply;
}
void Server::run(const string &filename, int port)
{
    string data = readMap(filename);
    try
    {
        boost::asio::io_service io_service;
        tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port));
        for (;;)
        {
          tcp::socket socket(io_service);
          acceptor.accept(socket);
          boost::asio::write(socket, boost::asio::buffer(header( data.size() )));
          boost::asio::write(socket, boost::asio::buffer(data));
        }
    }
    catch (std::exception& e)
    {
        cerr << "exception: " << e.what() << endl;
    }
}

Every time an error happens:

exception: Connection reset by peer

I can see some part of an image in my browser, and sometimes the image is almost complete, but it never works without the error.

If I use wget it looks like

wget http://localhost:8089
--2012-03-07 12:07:19--  http://localhost:8089/
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'

62% [========================================================>                                   ] 475,136     --.-K/s   in 0.002s  

2012-03-07 12:07:19 (287 MB/s) - Read error at byte 475136/760032 (Connection reset by peer). Retrying.

--2012-03-07 12:07:20--  (try: 2)  http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'

73% [==================================================================>                         ] 557,056     --.-K/s   in 0.001s  

... many failes and finally

--2012-03-07 12:09:01--  (try: 9)  http://localhost:8089/
Connecting to localhost|127.0.0.1|:8089... connected.
HTTP request sent, awaiting response... 200 OK
Length: 760032 (742K) [image/png]
Saving to: `index.html'

100%[===========================================================================================>] 760,032     --.-K/s   in 0.001s 

Any ideas how to fix it?

Upvotes: 3

Views: 8349

Answers (3)

Rawler
Rawler

Reputation: 1620

There are several more complete HTTP-implementations in the ASIO-docs, including static file serving. One way to go would be to reuse some of that sample code for your application.

In this particular case, there's an example of how to correctly open and buffer a file at http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/http/server/request_handler.cpp

  std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
    ...
  char buf[512];
  while (is.read(buf, sizeof(buf)).gcount() > 0)
    rep.content.append(buf, is.gcount());

The docs also has examples for actual asynchronous HTTP-implementations. (I assume you're using boost::asio to eventually make it asynchronous?)

Upvotes: 3

Mike Seymour
Mike Seymour

Reputation: 254501

You should receive and decode the HTTP request first, and only send the content if that was what was requested. Browsers sometimes request other resources as well; they may get upset if you send something unexpected, or if you send it before they've sent the request.

You also seem to have an off-by-one error in the data size - you put data.size()-1 in the header, and then send all of data. Perhaps this is a partial workaround for the bug in readMap, where you push an extra character after reaching EOF. You would be better off fixing that, by checking for eof() after reading but before pushing the character; or by reading in a less error-prone (and more efficient) manner, such as:

std::copy(std::istreambuf_iterator<char>(file),
          std::istreambuf_iterator<char>(), 
          std::back_inserter(data));

Also, I don't see any reason to copy the vector into a string. vector can also be converted to an asio::buffer.

Upvotes: 1

CashCow
CashCow

Reputation: 31445

Your way of reading the file is incorrect for a start.

Not simply that reading one character at a time isn't a great idea, but the loop is wrong. You could use istreambuf_iterator<char> to input or read() with a number of characters with gcount() determining when the read is complete.

Upvotes: 0

Related Questions