Stan Hebben
Stan Hebben

Reputation: 494

C++ duplex socket communication with asio

In Java and C# I used to be able to write bidirectional socket communication very easily by having a thread read incoming packets from a blocking stream, and other threads sending packets in a synchronized function.

For example something along the lines of:

class MyBidirectionalSocket {
    private OutputStream output;

    public MyBidirectionalSocket(Socket socket) {
        output = new BufferedOutputStream(socket.getOutputStream());
        new ReadingThread(socket).start();
    }

    public synchronized void sendPacket(MyPacket packet) {
        output.write(packet.getBytes());
        output.flush();
    }

    private class ReadingThread extends Thread {
        private InputStream input;

        private ReadingThread(Socket socket) {
             input = socket.getInputStream();
        }

        public void run() {
             // real code would catch EOF exceptions, IO exceptions, etc...
             while (true) {
                 MyPacket packet = readPacketFromStream(input);
                 doSomethingWithPacket(packet);
             }
        }
    }
}

That always worked pretty well for me, since I can implement readPacketFromStream by just reading data from the incoming stream without having to bother about when data arrives - it just blocks until data is available or throws an EOFException (or IOException) if the stream is closed, which I can catch later on. If I want high throughput, I could even distribute incoming packets along various worker threads.

Now my problem is - I would like to do something similar, in C++, with a cross-platform implementation. But how?

I have been studying the Boost libraries, since those seem to be widely used and have a lot of functionality. They offer essentially two ways to communicate with sockets, one stream-oriented blocking (using socket iostreams) and one using asynchronous socket communication (Boost asio).

The blocking method appears to only work half-duplex, since the iostream isn't thread-safe (and this multithreaded application can and will write and read at the same time!). Thus, as far as I'm aware, it is no option.

The asynchronous method appears to be the best option in my case. However, I will receive chunks of data - which might consist of a packet, a half packet, or multiple packets, or any combination thereof. Ideally, I would store the received data into a stream, from which a separate thread can then perform a blocking read.

Hence my question:

1) Does such multi-threaded "piping" stream exist in C++, either in the standard library, in Boost, or elsewhere? I am certainly not the first person in the world needing such a thing, so I suspect it must exist, though I couldn't find it.

2) Alternatively, is there another pattern - using Boost or another multi-platform library - that can be used to implement a mechanism similar to the code above?

Upvotes: 2

Views: 1283

Answers (1)

Matthias247
Matthias247

Reputation: 10396

boost::asio is very flexible. You can use it for synchronous operations (which block until data was sent or received) or asynchronous operations (where you start the read/write and will get a callback on a specific thread once it completes). You can even use a mix of both, e.g. reading asynchronously and writing synchronously.

If you want to follow your Java model as close as possible you could simply stick to the synchronous operations that asio provides.

That means if you have a socket like

shared_ptr<boost::asio::ip::tcp::socket> socket;

you startup an extra thread and use the blocking reads from there socket->read(...) and probably write to the socket from another thread with socket->write(...). That's absolutely safe as long as you don't do things like 2 concurrent reads. For concurrent writes you probably need an extra mutex.

Note that this synchronous writes/reads are only a very small wrapper around the native OS socket APIs which allow the same. asios power mostly comes in form of the async variants (which however would look different than your Java code and therefore might be less desirable). So you might even go with native sockets.

Regarding the higher level wrapper in asio, ip::tcp::iostream stream: I unfortunately also don't know whether this is thread safe or not and whether it can be used in the same bidirectional configuration as the synchronous sockets can. But I would expect it, because otherwise it would be quite worthless for most applications.

Upvotes: 1

Related Questions