dsollen
dsollen

Reputation: 6459

using boost:asio with select? blocking on TCP input OR file update

I had intended to have a thread in my program which would wait on two file descriptors, one for a socket and a second one for a FD describing the file system (specifically waiting to see if a new file is added to a directory). Since I expect to rarely see either the new file added or new TCP messages coming in I wanted to have one thread waiting for either input and handle whichever input is detected when it occures rather then bothering with seperate threads.

I then (finally!) got permission from the 'boss' to use boost. So now I want to replace the basic sockets with boost:asio. Only I'm running into a small problem. It seems like asio implimented it's own version of select rather then providing a FD I could use with select directly. This leaves me uncertain how I can block on both conditions, new file and TCP input, at the same time when one only works with select and the other doesn't seem to support the use of select. Is there an easy work around to this I'm missing?

Upvotes: 6

Views: 2782

Answers (1)

Cubbi
Cubbi

Reputation: 47468

ASIO is best used asynchronously (that's what it stands for): you can set up handlers for both TCP reads and the file descriptor activity, and the handlers would be called for you.

Here's a demo example to get you started (written for Linux with inotify support):

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <sys/inotify.h>

namespace asio = boost::asio;
void start_notify_handler();
void start_accept_handler();

// this stuff goes into your class, only global for the simplistic demo
asio::streambuf buf(1024);
asio::io_service io_svc;
asio::posix::stream_descriptor stream_desc(io_svc);
asio::ip::tcp::socket sock(io_svc);
asio::ip::tcp::endpoint end(asio::ip::tcp::v4(), 1234);
asio::ip::tcp::acceptor acceptor(io_svc, end);

// this gets called on file system activity
void notify_handler(const boost::system::error_code&,
                    std::size_t transferred)
{
    size_t processed = 0;
    while(transferred - processed >= sizeof(inotify_event))
    {
        const char* cdata = processed
                            + asio::buffer_cast<const char*>(buf.data());
        const inotify_event* ievent =
                                 reinterpret_cast<const inotify_event*>(cdata);
        processed += sizeof(inotify_event) + ievent->len;
        if(ievent->len > 0 && ievent->mask & IN_OPEN)
            std::cout << "Someone opened " << ievent->name << '\n';
    }
    start_notify_handler();
}

// this gets called when nsomeone connects to you on TCP port 1234
void accept_handler(const boost::system::error_code&)
{
    std::cout << "Someone connected from " 
              << sock.remote_endpoint().address() << '\n';
    sock.close(); // dropping connection: this is just a demo
    start_accept_handler();
}

void start_notify_handler()
{
    stream_desc.async_read_some( buf.prepare(buf.max_size()),
        boost::bind(&notify_handler, asio::placeholders::error,
                    asio::placeholders::bytes_transferred));
}

void start_accept_handler()
{
    acceptor.async_accept(sock,
        boost::bind(&accept_handler, asio::placeholders::error));
}

int main()
{
    int raw_fd = inotify_init(); // error handling ignored
    stream_desc.assign(raw_fd);
    inotify_add_watch(raw_fd, ".", IN_OPEN);
    start_notify_handler();
    start_accept_handler();
    io_svc.run();
}

Upvotes: 8

Related Questions