Monday, January 15, 2007

Unbuffered socket iostreams

Boost.Asio includes an iostreams-based interface to TCP sockets, ip::tcp::iostream, for simple use cases. However, like the file iostreams provided by the standard library, ip::tcp::iostream buffers input and output data. This can lead to problems if you forget to explicitly flush the stream. For example, consider the following code to perform an HTTP request:
ip::tcp::iostream stream("www.boost.org", "http");
stream << "GET / HTTP/1.0\r\n"
<< "Host: www.boost.org\r\n"
<< "\r\n";

std::string response_line;
std::getline(stream, response_line);
...
The code will be stuck on the getline() call waiting for the response, because the request will still be sitting stream's output buffer. The correct code looks like this:
ip::tcp::iostream stream("www.boost.org", "http");
stream << "GET / HTTP/1.0\r\n"
<< "Host: www.boost.org\r\n"
<< "\r\n"
<< std::flush;
The std::flush will cause the stream to send the entire contents of its output buffer at that point.

Boost.Asio now supports an alternative solution: turn off the stream's output buffering. This is accomplished as follows:
ip::tcp::iostream stream("www.boost.org", "http");
stream.rdbuf()->pubsetbuf(0, 0);
stream << "GET / HTTP/1.0\r\n"
<< "Host: www.boost.org\r\n"
<< "\r\n";
Now you can send and receive to your heart's content, without having to worry about whether your message is stuck in the output buffer, but be warned: an unbuffered stream is a lot less efficient in terms of system calls. Don't use this feature if you care about performance.