2011-01-18 8 views
21

私はライブラリをBoost.Asio(これまでのところうまくいきました)に変換しようとしていますが、私は設計上の決定に関して何かの障害にぶつかってきました。Boost.AsioでSSLソケットと非SSLソケットを同時に使用していますか?

Boost.AsioはSSLをサポートしていますが、ソケットにはboost::asio::ssl::stream<boost::asio::ip::tcp::socket>タイプを使用する必要があります。マイライブラリには、SSLサーバへの接続や、通常の接続のオプションを持っているので、私はこのような二つのソケットを持つクラスを作った:

class client : public boost::enable_shared_from_this<client> 
{ 
public: 
    client(boost::asio::io_service & io_service, boost::asio::ssl::context & context) : socket_(io_service), secureSocket_(io_service, context) {} 
private: 
    boost::asio::ip::tcp::socket socket_; 
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_; 
}; 

そして、そこの中socket_を参照するハンドラの束です。 (例えば、私はsocket_.is_open()をいくつかの場所に持っていますが、それは他のソケットのためにsecureSocket_.lowest_layer().is_open()になる必要があります)

誰もがこれについて最善の方法を提案できますか?私はむしろ、この目的のためだけに別のクラスを作成しないことにします。これは、多くのコードを複製することを意味するためです。

編集:私はOpenSSL機能の目的を誤解しているため、元の質問に言い換えました。

答えて

14

これを行うにはいくつかの方法があります。過去には、私はif/else文の多くでかなり迅速に散らかっ取得することができます

if (sslEnabled) 
    boost::asio::async_write(secureSocket_); 
} else { 
    boost::asio::async_write(secureSocket_.lowest_layer()); 
} 

ような何かをやりました。

class Socket 
{ 
    public: 
     virtual void connect(...); 
     virtual void accept(...); 
     virtual void async_write(...); 
     virtual void async_read(...); 
    private: 
     boost::asio::ip::tcp::socket socket_; 
}; 

が続いsecureSocket_代わりのsocket_上で動作する派生クラスSecureSocketを作成 - あなたはまた、抽象クラス(単純化の擬似コード)を作成することができます。私は多くのコードを複製するとは思わない、またはasync_writeが必要なときはいつも、if/elseよりもきれいだ。

+0

最初のアプローチは、私がやってしまったものですが、私は抽象クラスのアイデアのように実行します。

template <typename SocketType> void doStuffWithOpenSocket(SocketType socket) { boost::asio::write(socket, ...); boost::asio::read(socket, ...); boost::asio::read_until(socket, ...); // etc... } 

この関数は、通常のTCP ::ソケットとも安全なSSLソケットで作業を動作します。私はそれを見てみましょう - ありがとう。 – DSB

17

私はこの質問に答えるのに遅れていますが、これが他の人に役立つことを願っています。サムの答えにはアイデアの虫が含まれていますが、私の意見では十分に遠ざかりません。

このアイデアは、asioがSSLソケットをストリーム内にラップするという考えから来ています。このすべての解決策は、非SSLソケットを同様にラップすることです。

SSLソケットと非SSLソケットの間に一様な外部インターフェイスがあるという望ましい結果は、3つのクラスで行われます。一方のベースは、実質的にインターフェイスを定義します。

class Socket { 
public: 
    virtual boost::asio::ip::tcp::socket &getSocketForAsio() = 0; 

    static Socket* create(boost::asio::io_service& iIoService, boost::asio::ssl::context *ipSslContext) { 
     // Obviously this has to be in a separate source file since it makes reference to subclasses 
     if (ipSslContext == nullptr) { 
      return new NonSslSocket(iIoService); 
     } 
     return new SslSocket(iIoService, *ipSslContext); 
    } 

    size_t _read(void *ipData, size_t iLength) { 
     return boost::asio::read(getSocketForAsio(), boost::asio::buffer(ipData, iLength)); 
    } 
    size_t _write(const void *ipData, size_t iLength) { 
     return boost::asio::write(getSocketForAsio(), boost::asio::buffer(ipData, iLength)); 
    } 
}; 

2つのサブクラスがSSLソケットと非SSLソケットをラップします。

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SslSocket_t; 
class SslSocket: public Socket, private SslSocket_t { 
public: 
    SslSocket(boost::asio::io_service& iIoService, boost::asio::ssl::context &iSslContext) : 
     SslSocket_t(iIoService, iSslContext) { 
    } 

private: 
    boost::asio::ip::tcp::socket &getSocketForAsio() { 
     return next_layer(); 
    } 
}; 

class NonSslSocket: public Socket, private Socket_t { 
public: 
    NonSslSocket(boost::asio::io_service& iIoService) : 
      Socket_t(iIoService) { 
    } 

private: 
    boost::asio::ip::tcp::socket &getSocketForAsio() { 
     return next_layer(); 
    } 
}; 

あなたがgetSocketForAsio()を使用ASIO関数を呼び出すのではなく、Socketオブジェクトへの参照を渡すたびに。例:

boost::asio::async_read(pSocket->getSocketForAsio(), 
      boost::asio::buffer(&buffer, sizeof(buffer)), 
      boost::bind(&Connection::handleRead, 
        shared_from_this(), 
        boost::asio::placeholders::error, 
        boost::asio::placeholders::bytes_transferred)); 

ソケットがポインタとして格納されていることに注意してください。多形性がどのように隠されているのか、私は考えられません。

ペナルティは、非SSLソケットを取得するために使用される余分な間接レベルです。

+0

は 'Socket_t'の' typedef'のように思えます。また、なぜこれが動作するのか、つまり 'next_layer()'がソケットの種類ごとに何をするのかを説明できますか? – yhager

+0

訂正していただきありがとうございます。 – Nicole

+1

@yhager、私がこの作業をしたのは、SSLソケットが非SSLソケットから派生する方法をコピーすることです。 AsioはSSLソケットをいくつかの追加機能を提供するコンテナにラップして作成し、SSLフレーバを追加していくつかのアクティビティをインターセプトします。このようなSSLソケットを使用するコードは、 'next_layer()'を使用して、SSLソケットと非SSLソケットの両方に共通するいくつかのアクティビティのために、基礎となるソケットにアクセスする必要があります。 私が提案しているのは、ソケットをSSLソケットのように扱い、SSL関連のアクティビティを実装しないでソケットをラップすることです。 – Nicole

1

それはこのようなものを使用してコンパイルします:もちろんの問題は、TCP ::ソケットとSSL「ソケット」は、任意の共通の祖先を共有していないということです

typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> Socket_t;

3

。しかし、いったん開いてしまえば、ソケットを使うためのほとんどの関数はまったく同じ構文を共有します。もっともクリーンなソリューションはテンプレートです。

boost::asio::ip::tcp::socket socket_; 
// socket_ opened normally ... 
doStuffWithOpenSocket<boost::asio::ip::tcp::socket>(socket_); // works! 

boost::asio::ssl::stream<boost::asio::ip::tcp::socket> secureSocket_; 
// secureSocket_ opened normally (including handshake) ... 
doStuffWithOpenSocket(secureSocket_); // also works, with (different) implicit instantiation! 
// shutdown the ssl socket when done ...