これは、SSLSockets クラスでソケット接続をシャットダウンするために使用するコードです。これは、SSL 接続用の ASIO のラッパーでもあります。Windows 用 Boost ASIO バージョン 1.52 と 32 ビット ライブラリを使用しています。私も、適切に行う方法を発見するまで、ソケットをシャットダウンするときに例外を受け取っていました。
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();
}