1

WindowsでBoost::ASIOバージョン1.52.0を使用し、VC++でVisualStudio2008コードを使用してWin8を実行しているテストマシンでSSLバージョンCを使用しています。

指定されたURLでTCPサーバーに正常に接続するコードがいくつかあります。

// Create the resolver and query objects to resolve the host name in serverPath to an ip address.
boost::asio::ip::tcp::resolver resolver(*IOService);
boost::asio::ip::tcp::resolver::query query(serverPath, port);
boost::asio::ip::tcp::resolver::iterator EndpointIterator = resolver.resolve(query);
// Set up an SSL context.
boost::asio::ssl::context ctx(*IOService, boost::asio::ssl::context::tlsv1_client);
// Specify to not verify the server certificiate right now.
ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
// Init the socket object used to initially communicate with the server.
pSocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(*IOService, ctx);
//
// The thread we are on now, is most likely the user interface thread.  Create a thread to handle all incoming socket work messages.
// This thread is created for each connection to a server.
if (!RcvThreadCreated)
{
   WorkerThreads.create_thread(boost::bind(&SSLSocket::RcvWorkerThread, this));
   RcvThreadCreated = true;
   WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread, this));
}
// Try to connect to the server.  Note - add timeout logic at some point.
boost::asio::async_connect(pSocket->lowest_layer(), EndpointIterator,
   boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholders::error));

いくつかの調査に基づいて、IPアドレスをserverPathのURLに置き換えた場合、IPアドレスを解決する上記のコードは使用できません。正しいアプローチは、次のようなエンドポイントを作成することのようです。

const boost::asio::ip::address IP(boost::asio::ip::address::from_string(serverPath));
int iport = atoi(port.c_str());
boost::asio::ip::tcp::endpoint EP(IP, iport);

この部分は正常にコンパイルされているようです。ただし、後でasync_connectメソッドで次のようになります。

// Try to connect to the server.  Note - add timeout logic at some point.
boost::asio::async_connect(pSocket->lowest_layer(), EP,
boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholders::error));

エラーが発生します。async_connectメソッドが2番目の引数のイテレータを見たいと思っていると思います。でも、どうやって作るのかわかりません。

次のリンクによると、答えは、ソケットオブジェクトのconnectメソッドを使用できることを示唆しています。しかし、私がそれをしようとすると:

pSocket->connect(EP);

コンパイラはエラーを出します-エラーC2039:

'connect' : is not a member of 'boost::asio::ssl::stream<Stream>'

それで、誰かがURLの代わりにIPアドレスでサーバーに接続する方法を提供するいくつかの簡単なコードを見せてもらえますか?

それが不可能な場合は、IPアドレスから逆引き参照を実行してURLを取得する方法はありますか?

4

2 に答える 2

1

インスタンスをバインドせずにメンバー関数ポインターを渡しているため、コードの async_connect() 行はコンパイルされていません。コード内の他の場所でメンバー関数を適切にバインドしたので、見落としだと思います。正しい行は次のようになります。

pSocket->next_layer().async_connect(
    EP,
    boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholder::error));

元の問題に関しては、IP アドレス クエリで ip::tcp::resolver を使用して IP アドレスのエンドポイントを取得すると問題なく動作するため、名前とアドレスに個別のコード パスを用意する必要はありません。ssl::stream で lower_layer() を使用して、基になる ip::tcp::socket にアクセスするのが、connect() および async_connect() にアクセスする正しい方法です。

ssl::context を使用して ssl::stream を作成し、接続が確立された後に async_handshake() を使用することを除けば、SSL コードは TCP コードとほぼ同じに見えるはずです。TCP コードで lower_layer() を使用することもできます (それ自体が返されるだけです)。これにより、類似点が強調されます。

作業コードから簡略化したため、このコードのコンパイルは保証されませんが、async_resolve() を実行すると、解決ハンドラー メソッドは (sp_ソケット/ストリーム ポインター) のようになります。

void Pimpl::handleResolve(
  const boost::system::error_code& ec,
  boost::asio::ip::tcp::resolver::iterator endpointIterator) {
  if (ec) {
    // error handling here
    return;      
  }
  sp_->lowest_layer().async_connect( 
     *endpointIterator,
     boost::bind(&Pimpl::handleConnect, this, endpointIterator, boost::asio::placeholder::error));
}

次に、接続ハンドラーは次のようになります (これは、機能するエンドポイントを取得するか、エンドポイントがなくなるまでエンドポイントを試行します)。

void Pimpl::handleConnect(
  boost::asio::ip::tcp::resolver::iterator endpointIterator,
  const boost::system::error_code& ec) {
  if (!ec) {
    sp_->async_handshake(
      boost::asio::ssl::stream_base::client,
      boost::bind(&Pimpl::handleHandshake, this, boost::asio::placeholder::error));
  }
  else if (++endpointIterator != boost::asio::ip::tcp::resolver::iterator()) {
    // Try connecting to the next endpoint.
    sp_->lowest_layer().close();
    sp_->lowest_layer().async_connect(
      *endpointIterator,
      boost::bind(&Pimpl::handleConnect, this, endpointIterator, boost::asio::placeholder::error));
  }
  else {
    // error handling here
  }
}

これを ip::tcp::socket ポインターではなく ssl::stream ポインターに固有にする唯一の行は、async_handshake() 呼び出しです。

于 2013-02-28T23:52:10.207 に答える
0

この問題の回避策を見つけました。ssl::stream クラスをソケット オブジェクトとして使用していますが、エラーの理由は接続メソッドがないためです。ただし、connect または async_connect メソッドを呼び出すことができるオブジェクトを返す next_layer() メソッドがあります。async_connect をコンパイルするのに問題がありましたが、connect メソッドは機能します。その後、次のように HandleConnect メソッドを呼び出します。

boost::system::error_code EC;
pSocket->next_layer().connect(EP, EC);
HandleConnect(EC);

コードに対する@SamMillerリクエストへの回答として、以下に示します。boost::asio::async_connect 呼び出しのエンドポイントのイテレータを作成する方法と、 next_layer オブジェクトの async_connect メソッドを呼び出す方法を知りたいです。

void SSLSocket::Connect(SSLSocket* psSLS, const string& serverPath, string& port)
{
   // Connects to the server.
   // serverPath - specifies the path to the server.  Can be either an ip address or url.
   // port - port server is listening on.
   //
   try
   {
      Locking CodeLock(SocketLock); // Single thread the code.
      // If the user has tried to connect before, then make sure everything is clean before trying to do so again.
      if (pSocket)
      {
         delete pSocket;
         pSocket = 0;
      }                                                                                                  
      // If serverPath is a URL, then resolve the address.
      if ((serverPath[0] < '0') || (serverPath[0] > '9')) // Assumes that the first char of the server path is not a number when resolving to an ip addr.
      {
         // Create the resolver and query objects to resolve the host name in serverPath to an ip address.
         boost::asio::ip::tcp::resolver resolver(*IOService);
         boost::asio::ip::tcp::resolver::query query(serverPath, port);
         boost::asio::ip::tcp::resolver::iterator EndpointIterator = resolver.resolve(query);
         // Set up an SSL context.
         boost::asio::ssl::context ctx(*IOService, boost::asio::ssl::context::tlsv1_client);
         // Specify to not verify the server certificiate right now.
         ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
         // Init the socket object used to initially communicate with the server.
         pSocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(*IOService, ctx);
         //
         // The thread we are on now, is most likely the user interface thread.  Create a thread to handle all incoming socket work messages.
         // This thread is created for each connection to a server.
         if (!RcvThreadCreated)
         {
            WorkerThreads.create_thread(boost::bind(&SSLSocket::RcvWorkerThread, this));
            RcvThreadCreated = true;
            WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread, this));
         }
         // Try to connect to the server.  Note - add timeout logic at some point.
         boost::asio::async_connect(pSocket->lowest_layer(), EndpointIterator,
            boost::bind(&SSLSocket::HandleConnect, this, boost::asio::placeholders::error));
      }
      else
      {
         // serverPath is an ip address, so try to connect using that.
         //
         // Create an endpoint with the specified ip address.
         const boost::asio::ip::address IP(boost::asio::ip::address::from_string(serverPath));
         int iport = atoi(port.c_str());
         const boost::asio::ip::tcp::endpoint EP(IP, iport);
         // Set up an SSL context.
         boost::asio::ssl::context ctx(*IOService, boost::asio::ssl::context::tlsv1_client);
         // Specify to not verify the server certificiate right now.
         ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
         // Init the socket object used to initially communicate with the server.
         pSocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(*IOService, ctx);
         //
         // The thread we are on now, is most likely the user interface thread.  Create a thread to handle all incoming socket work messages.
         // This thread is created for each connection to a server.
         if (!RcvThreadCreated)
         {
            WorkerThreads.create_thread(boost::bind(&SSLSocket::RcvWorkerThread, this));
            RcvThreadCreated = true;
            WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread, this));
         }
         // Try to connect to the server.  Note - add timeout logic at some point.
         // pSocket->next_layer().async_connect(EP, &SSLSocket::HandleConnect);
         boost::system::error_code EC;
         pSocket->next_layer().connect(EP, EC);
         if (EC)
         {
            // Log an error.  This worker thread should exit gracefully after this.
            stringstream ss;
            ss << "SSLSocket::Connect: connect failed to " << sClientIp << " : " << uiClientPort << ".  Error: " << EC.message() + ".\n";
            Log.LogString(ss.str(), LogError);
         }
         HandleConnect(EC);
      }
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::Connect: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

これは、次の行の場合に生成されるエラーです。

pSocket->next_layer().async_connect(EP, &SSLSocket::HandleConnect); // Does not compile

は次のように置き換えられます:

boost::system::error_code EC;
pSocket->next_layer().connect(EP, EC); // Compiles and works properly.

エラーメッセージ:

1>c:\boost_1_52_0\boost\asio\basic_socket.hpp(706) : error C2064: term does not evaluate to a function taking 1 arguments
1>        c:\users\bob\documents\visual studio 2008\projects\attackpoker1\win32client\sslsockets\sslsocket.cpp(114) : see reference to function template instantiation 'void boost::asio::basic_socket<Protocol,SocketService>::async_connect<void(__thiscall SSLSocket::* )(const boost::system::error_code &)>(const boost::asio::ip::basic_endpoint<InternetProtocol> &,const ConnectHandler &)' being compiled
1>        with
1>        [
1>            Protocol=boost::asio::ip::tcp,
1>            SocketService=boost::asio::stream_socket_service<boost::asio::ip::tcp>,
1>            InternetProtocol=boost::asio::ip::tcp,
1>            ConnectHandler=void (__thiscall SSLSocket::* )(const boost::system::error_code &)
1>        ]
于 2013-02-22T18:02:51.807 に答える