C++ 与 asio 的双工套接字通信

C++ duplex socket communication with asio

在 Java 和 C# 中,我曾经能够非常轻松地编写双向套接字通信,方法是让一个线程从阻塞流中读取传入的数据包,并让其他线程在同步函数中发送数据包。

例如:

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);
             }
        }
    }
}

这对我来说一直很有效,因为我可以通过仅从传入流中读取数据来实现 readPacketFromStream,而不必担心数据何时到达 - 它只是阻塞直到数据可用或抛出 EOFException(或 IOException)如果流已关闭,我稍后可以捕获。如果我想要高吞吐量,我什至可以沿着不同的工作线程分发传入的数据包。

现在我的问题是 - 我想在 C++ 中通过跨平台实现做一些类似的事情。但是怎么办?

我一直在研究 Boost 库,因为它们似乎被广泛使用并且具有很多功能。它们本质上提供了两种与套接字通信的方式,一种是面向流的阻塞(使用套接字 iostreams),另一种是使用异步套接字通信(Boost asio)。

阻塞方法似乎只能半双工工作,因为 iostream 不是线程安全的(而且这个多线程应用程序可以并且将同时写入和读取!)。因此,据我所知,这是别无选择。

异步方法似乎是我的最佳选择。但是,我将收到数据块——可能由一个数据包、半个数据包或多个数据包或它们的任意组合组成。 理想情况下,我会将接收到的数据存储到一个流中,然后一个单独的线程可以从该流中执行阻塞读取。

因此我的问题是:

1) C++中是否存在这样的多线程"piping"流,标准库中,Boost中,或其他地方?我当然不是第一个世界上有人需要这样的东西,所以我怀疑它一定存在,虽然我找不到它。

2) 或者,是否有另一种模式 - 使用 Boost 或其他多平台库 - 可用于实现类似于上述代码的机制?

boost::asio 非常灵活。您可以将它用于同步操作(在发送或接收数据之前阻塞)或异步操作(您启动 read/write 并在完成后在特定线程上获得回调)。您甚至可以混合使用两者,例如异步读取和同步写入。

如果您想尽可能地遵循您的 Java 模型,您可以简单地坚持使用 asio 提供的同步操作。

这意味着如果你有一个像

这样的插座
shared_ptr<boost::asio::ip::tcp::socket> socket;

你启动了一个额外的线程并从那里使用阻塞读取 socket->read(...) 并且可能使用 socket->write(...) 从另一个线程写入套接字。只要您不执行 2 个并发读取之类的操作,那是绝对安全的。对于并发写入,您可能需要一个额外的互斥量。

请注意,此同步 writes/reads 只是本机 OS 套接字 API 的一个非常小的包装器,它允许相同的操作。 asios 的强大功能主要以异步变体的形式出现(但是它看起来与您的 Java 代码不同,因此可能不太理想)。所以你甚至可以使用本机套接字。

关于 asio 中更高级别的包装器,ip::tcp::iostream stream: 不幸的是,我也不知道这是否是线程安全的,以及它是否可以在与同步套接字相同的双向配置中使用。但我会期待它,因为否则它对大多数应用程序来说将毫无价值。