0

在建立并使用成功连接后删除 SSLSocket 时,我遇到了访问冲突

Application_client_example.exe 中 0x770f32d0 处的未处理异常:0xC0000005:访问冲突读取位置 0x00000dd3c0c76c48。

访问冲突来自这部分 boost 代码:

engine::~engine()
{
  if (SSL_get_app_data(ssl_))
  {
    delete static_cast<verify_callback_base*>(SSL_get_app_data(ssl_));
    SSL_set_app_data(ssl_, 0);
  }

  ::BIO_free(ext_bio_);
  ::SSL_free(ssl_);
}

此代码在 boost 1.47 版中有效。我所做的唯一更改是将 boost 库更新到当前版本 1.53 并构建了库和 exe 的 64 位版本。

这是创建和删除的 SSL 连接:

// Connect 
    SSLSocket* socket = new SSLSocket();

    if ((errorCode = socket->connect((char*)server.c_str(), (char*)port.c_str())) != 0) 
    {
        Logger::log(log4cpp::Priority::FATAL, "Secure Socket Error");

        return errorCode;
    }    

    delete socket

这是 SSLSocket 析构函数

SSLSocket::~SSLSocket(void)
{
    try {
        sslSocket->shutdown();
        delete sslSocket;
    }
    catch (std::exception& e)
    {
        std::string exception(e.what());
        Logger::log(log4cpp::Priority::FATAL, "[SSLSocket] Error deleting sslSocket. Exception: " + exception);
    }

}

这是 SSLSocket 的定义。SSLSocket 本质上只是 ssl 套接字的包装类:

#ifndef __SSLSOCKET__
#define __SSLSOCKET__

#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/asio/ssl.hpp>
#include <string>

#include "Logger.h"
#include "Config.h"

using namespace boost::asio;

class SSLSocket
{
private:
    io_service io_service;
    ssl::stream<ip::tcp::socket>* sslSocket;

public:
    SSLSocket(void);
    ~SSLSocket(void);

    DWORD connect(char* remoteServer, char* remotePort);
    DWORD sendString(std::string data);
    std::string receiveString(void);
};

#endif
4

2 回答 2

0

这是我用来关闭 SSLSockets 类中的套接字连接的代码,它也是用于 SSL 连接的 ASIO 包装器。我正在使用带有 32 位库的 Windows Boost ASIO 1.52 版。我也曾经在关闭套接字时收到异常,直到我发现如何正确执行此操作:

void SSLSocket::Stop()
{
   // This method calls the shutdown method on the socket in order to stop reads or writes that might be going on.  If this is not done, then an exception will be thrown
   // when it comes time to delete this object.
   //
   boost::system::error_code EC;
   try
   {
      // This method can be called from the handler as well.  So once the ShuttingDown flag is set, don't go throught the same code again.
      if (ShuttingDown)
         return;
      LockCode->Acquire(); // Single thread the code.
      // Only do this once.
      if (!ShuttingDown)
      {
         ShuttingDown = true;
         pSocket->next_layer().cancel();
         pSocket->shutdown(EC);
         // Note that EC will usually have an error condition, but it does not seem to be a problem.
         delete pSocket;
         pSocket = 0;
         ReqAlive = false;
         SetEvent(hEvent);
         IOService->stop();
         LobbySocketOpen = false;
         // Wait until the 2 threads have exited before returning.
         WorkerThreads.join_all();
      }
      LockCode->Release();
      delete LockCode;
      LockCode = 0;
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::Stop: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
}

回答有关 Lock 变量的问题

Lock 是一个封装了临界区(Microsoft 特有)的类,以便代码可以是单线程的。这是它的定义:

class Lock
{
public:
   Lock()
   {
      ::InitializeCriticalSection(&CS);
   }

   ~Lock()
   {
      ::DeleteCriticalSection(&CS);
   }

   void Acquire()
   {
      ::EnterCriticalSection(&CS);
   }

   void Release()
   {
      ::LeaveCriticalSection(&CS);
   }

private:
   Lock(const Lock&);
   Lock& operator=(const Lock&);
   CRITICAL_SECTION CS;
};

套接字创建代码

这是我用来创建 SSL 上下文对象和 SSL 套接字对象的代码:

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
   {
      LockCode->Acquire(); // 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.
         // Only one thread is created to handle the socket I/O reading and another thread is created to handle writing.
         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.
         //
         stringstream ss1;
         boost::system::error_code EC;
         ss1 << "SSLSocket::Connect: Preparing to connect to game server " << serverPath << " : " << port << ".\n";
         Log.LogString(ss1.str(), LogInfo);
         // 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);
         //
         // Try to connect to the server.  Note - add timeout logic at some point.
         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);
         }
         stringstream ss;
         ss << "SSLSocket::Connect: Calling HandleConnect for game server " << serverPath << " : " << port << ".\n";
         Log.LogString(ss.str(), LogInfo);
         HandleConnect(EC);
      }
   }
   catch (std::exception& e)
   {
      stringstream ss;
      ss << "SSLSocket::Connect: threw an error - " << e.what() << ".\n";
      Log.LogString(ss.str(), LogError);
      Stop();
   }
   LockCode->Release();
}
于 2013-05-03T01:41:52.150 回答
0

好的,所以在搜索并没有真正找到任何相关内容后解决这个问题的方法是,我将所有的 boost 库都构建为 MTd。我假设我会为 OpenSSL 库使用 MTd 构建,但来看看你是否使用非 MT Openssl 库它工作得很好。

于 2013-05-10T17:51:13.100 回答