2016-10-02 17 views
0

私は、ブーストASIOとコルーチンを使って安全なSSLエコーサーバを作成しています。理論的にはio_serviceマネージャに制御を戻しますasync_acceptを呼び出す:私は、このサーバーは、同時に複数のクライアントにサービスを提供できるようにしたいのですが、これは、私は上記のコードがどうなるかはわからない。とにかく私のコードasync_acceptで複数のクライアントを扱う

try { 
    boost::asio::io_service io_service; 

    boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) { 
     auto ctx = boost::asio::ssl::context{ boost::asio::ssl::context::sslv23 }; 
     ctx.set_options(
     boost::asio::ssl::context::default_workarounds 
     | boost::asio::ssl::context::no_sslv2 
     | boost::asio::ssl::context::single_dh_use); 
     ctx.use_private_key_file(..); // My data setup 
     ctx.use_certificate_chain_file(...); // My data setup 

     boost::asio::ip::tcp::acceptor acceptor(io_service, 
     boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)); 

     for (;;) { 

     boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sock{ io_service, ctx }; 
     acceptor.async_accept(sock.next_layer(), yield); 

     sock.async_handshake(boost::asio::ssl::stream_base::server, yield); 

     auto ec = boost::system::error_code{}; 
     char data_[1024]; 
     auto nread = sock.async_read_some(boost::asio::buffer(data_, 1024), yield[ec]); 

     if (ec == boost::asio::error::eof) 
      return; //connection closed cleanly by peer 
     else if (ec) 
      throw boost::system::system_error(ec); //some other error, is this desirable? 

     sock.async_write_some(boost::asio::buffer(data_, nread), yield[ec]); 

     if (ec == boost::asio::error::eof) 
      return; //connection closed cleanly by peer 
     else if (ec) 
      throw boost::system::system_error(ec); //some other error 

     // Shutdown gracefully 
     sock.async_shutdown(yield[ec]); 
     if (ec && (ec.category() == boost::asio::error::get_ssl_category()) 
      && (SSL_R_PROTOCOL_IS_SHUTDOWN == ERR_GET_REASON(ec.value()))) 
     { 
      sock.lowest_layer().close(); 
     } 
     } 

    }); 

    io_service.run(); 
    } 
    catch (std::exception& e) 
    { 
    std::cerr << "Exception: " << e.what() << "\n"; 
    } 

です。

既に受け入れられている、つまりすでにasync_accept行を過ぎている場合、別の接続を受け入れることができますか?

答えて

1

コードが不完全であるため(ブロック内にreturnがありますが、ブロックの部分が何であるか不明です)、質問の内容を理解するのは少し難しいです。

にもかかわらず、ドキュメントにはexample of a TCP echo server using coroutinesが含まれています。基本的には、SSLサポートを必要に応じて追加する必要があります。

あなたはmainを見れば、それは次のチャンクがあります

boost::asio::spawn(io_service, 
    [&](boost::asio::yield_context yield) 
    { 
     tcp::acceptor acceptor(io_service, 
     tcp::endpoint(tcp::v4(), std::atoi(argv[1]))); 

     for (;;) 
     { 
     boost::system::error_code ec; 
     tcp::socket socket(io_service); 
     acceptor.async_accept(socket, yield[ec]); 
     if (!ec) std::make_shared<session>(std::move(socket))->go(); 
     } 
    }); 

これは無限ループ、および、async_acceptにそれぞれ(成功した)呼び出しの後、この接続と他の人がかもしれませんが(次の接続を受け入れる扱いまだアクティブである)。

繰り返しますが、私はあなたのコードについてはよく分からないが、それはここで、点を説明するため

return; //connection closed cleanly by peer 

のようなループからの出口が含まれている2つのアプリケーションです。

最初はPMOTWから適応エコークライアントをマルチプロセッシングのPython、次のとおりです。

import socket 
import sys 
import multiprocessing 

def session(i): 
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

    server_address = ('localhost', 5000) 
    print 'connecting to %s port %s' % server_address 
    sock.connect(server_address) 
    print 'connected' 

    for _ in range(300): 
     try: 

      # Send data 
      message = 'client ' + str(i) + ' message' 
      print 'sending "%s"' % message 
      sock.sendall(message) 

      # Look for the response 
      amount_received = 0 
      amount_expected = len(message) 

      while amount_received < amount_expected: 
       data = sock.recv(16) 
       amount_received += len(data) 
       print 'received "%s"' % data 

     except: 
      print >>sys.stderr, 'closing socket' 
      sock.close() 

if __name__ == '__main__': 
    pool = multiprocessing.Pool(8) 
    pool.map(session, range(8)) 

詳細は、(それはPythonの、と読みが容易であるが)それは重要ではありませんが、ポイントは、それが開くということです8つのプロセスで構成され、それぞれが300個のメッセージを持つ同じエコーエコーサーバ(以下)を使用します。実行

は、それがエコーセッションがインターリーブされた確かにあることを示す

... 
received "client 1 message" 
sending "client 1 message" 
received "client 2 message" 
sending "client 2 message" 
received "client 3 message" 
received "client 0 message" 
sending "client 3 message" 
sending "client 0 message" 
... 

出力します。

エコーサーバ用です。私は少しexample from the docsを適応してきました:

#include <cstdlib> 
#include <iostream> 
#include <memory> 
#include <utility> 
#include <boost/asio.hpp> 

using boost::asio::ip::tcp; 

class session : 
    public std::enable_shared_from_this<session> { 

public: 
    session(tcp::socket socket) : socket_(std::move(socket)) {} 

    void start() { do_read(); } 

private: 
    void do_read() { 
     auto self(
      shared_from_this()); 
     socket_.async_read_some(
      boost::asio::buffer(data_, max_length), 
      [this, self](boost::system::error_code ec, std::size_t length) { 
       if(!ec) 
        do_write(length); 
      }); 
    } 

    void do_write(std::size_t length) { 
     auto self(shared_from_this()); 
     socket_.async_write_some(
      boost::asio::buffer(data_, length), 
      [this, self](boost::system::error_code ec, std::size_t /*length*/) { 
       if (!ec) 
        do_read(); 
      }); 
    } 

private: 
    tcp::socket socket_; 
    enum { max_length = 1024 }; 
    char data_[max_length]; 
}; 

class server { 
public: 
    server(boost::asio::io_service& io_service, short port) : 
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), 
      socket_(io_service) { 
     do_accept(); 
    } 

private: 
    void do_accept() { 
     acceptor_.async_accept(
      socket_, 
      [this](boost::system::error_code ec) { 
       if(!ec) 
        std::make_shared<session>(std::move(socket_))->start(); 

       do_accept(); 
      }); 
    } 

    tcp::acceptor acceptor_; 
    tcp::socket socket_; 
}; 

int main(int argc, char* argv[]) { 
    const int port = 5000; 
    try { 
     boost::asio::io_service io_service; 

     server s{io_service, port}; 

     io_service.run(); 
    } 
    catch (std::exception& e) { 
     std::cerr << "Exception: " << e.what() << "\n"; 
    } 
} 

これは、このサーバーが実際にインターリーブすることを示しています。

これはではありません。コルーチンバージョンです。私はかつてコルーチン版で少し遊んでいましたが、私の現在のボックスにビルドすることができませんでした(また、以下のコメントのseheノートとして、今のところこの主流バージョンを好むかもしれません)。

ただし、これは基本的な違いではありません。あなたの質問。非コルーチンバージョンでは、明示的に次のコールバックを提供する新しい操作を明示的に起動するコールバックがあります。コルーチンバージョンでは、より逐次的に見えるパラダイムが使用されます。各コールは、両方のバージョンでasioの制御ループに戻ります。これは、すべての処理を続行できます。 asio coroutine docsから

コルーチンを使用すると、実際のプログラム・ロジックを反映した構造を作成してみましょう。非同期操作が完了したときに何が起こるべきかを定義するハンドラーがないため、非同期操作は関数を分割しません。ハンドラをお互いに呼び出す代わりに、プログラムはシーケンシャル構造を使用できます。

これは、シーケンシャル構造は、すべての操作がシーケンシャルになることはありません - asioのための全体の必要性を根絶するだろうと。

+0

これはコルーチンを使用していることに注意することが重要です。これは典型的な使用例ではありません(まだ)。それは生涯の構造を劇的に変えます。特に、stackfulコルーチン – sehe

+0

Haを使用しない限り、ソケットのローカル変数で 'async_accept'を使うことは大きな問題です。私は次回のタイトルを超えて質問を読むべきです:)私は少し速い答えにスキップしました。 (私は通常、ソケットを 'session'のメンバにするので、両方の方法で使いやすいです)。 +1 – sehe

+0

ありがとう、このコードはちょうどmain()ルーチンにあります。私もそれを投稿しておくべきだった、申し訳ありません。私のコードが動作するかどうかはまだ分かりません:リンクした例の 'go'関数のように別の' boost :: spawn'を呼び出さなければなりませんか?これらはスレッドではなく、私は混乱しています..着信接続はバッファされていますか? 2番目のコルーチンを生成すると、他の接続を処理できますか? – Dean

関連する問題