既存のサーバーと通信するために作成している新しいクライアントに、Boost1.52.032ビットライブラリとOpenSSL32ビットライブラリとアンマネージドVisualC++2008を使用しています。私のテストマシンはWindows8を使用しています。同期読み取りと書き込みを使用しています。コードはC#からアクセスされるDLLに組み込まれていますが、すべてのasio呼び出しは、boost::thread_groupで作成されたアンマネージスレッドで実行されます。
私が発見したのは、同期読み取りがデータを待機しているとき、別のスレッドで行われている同期書き込みはブロックされているように見え、消えないということです-少なくとも私がコーディングした方法では。だから私の質問は-同期読み取りが別のスレッドでデータを待っている間に同期書き込みを完全に実行できるべきですか?
別のスレッドで保留中の読み取りがない場合に、データを正常に書き出すことができることを確認しました。これを行うには、読み取りが行われる直前に、読み取りが行われていたスレッドをフリーズします。次に、書き込み用のスレッドがメッセージを書き出しました。次に、読み取りスレッドを解凍すると、送信されたメッセージに関するサーバーからの応答を正常に読み取ることができました。
次のメソッドは、サーバーからのネットワークからのメッセージの読み取りを処理するためにcreate_threadメソッドによって呼び出されます。
void SSLSocket::ProcessServerRequests()
{
// This method is responsible for processing requests from a server.
Byte *pByte;
int ByteCount;
size_t BytesTransferred;
boost::system::error_code Err;
Byte* pReqBuf;
string s;
stringstream ss;
//
try
{
ss << "ProcessServerRequests: Worker thread: " << Logger::NumberToString(boost::this_thread::get_id()) << " started.\n";
Log.LogString(ss.str(), LogInfo);
// Enable the handlers for the handshaking.
IOService->run();
// Wait for the handshake to be sucessfully completed.
do
{
Sleep(50);
} while (!HandShakeReady);
//
sClientIp = pSocket->lowest_layer().remote_endpoint().address().to_string();
uiClientPort = pSocket->lowest_layer().remote_endpoint().port();
ReqAlive = true;
// If the thread that handles sending msgs to all servers has not been created yet, then create that one.
// This thread is created just once to handle all outbound msgs to all servers.
WorkerThreads.create_thread(boost::bind(&SSLSocket::SendWorkerThread));
// Loop until the user quits, or an error is detected. The read method should wait until there is something to read.
do
{
pReqBuf = BufMang.GetPtr(MsgLenBytes);
boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, MsgLenBytes), boost::asio::transfer_exactly(MsgLenBytes), Err);
if (Err)
{
s = Err.message();
if ((s.find("short r")) == string::npos)
{
ss.str("");
ss << "SSLSocket::ProcessServerRequests: read(1) error = " << Err.message() << "\n. Terminating.\n\n";
Log.LogString(ss.str(), LogError);
}
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
}
else
{
// Get the number of bytes in the message.
pByte = pReqBuf;
B2I.B.B1 = *pByte++;
B2I.B.B2 = *pByte++;
B2I.B.B3 = *pByte++;
B2I.B.B4 = *pByte;
ByteCount = B2I.IntVal;
pReqBuf = BufMang.GetPtr(ByteCount);
// Do a synchronous read which will hang until the entire message is read off the wire.
BytesTransferred = boost::asio::read(*pSocket, boost::asio::buffer(pReqBuf, ByteCount), boost::asio::transfer_exactly(ByteCount), Err);
ss.str("");
ss << "SSLSocket::ProcessServerRequests: # bytes rcvd = " << Logger::NumberToString(BytesTransferred).c_str() << " from ";
ss << sClientIp.c_str() << " : " << Logger::NumberToString(uiClientPort) << "\n";
Log.LogString(ss.str(), LogDebug2);
Log.LogBuf(pReqBuf, (int)BytesTransferred, DisplayInHex, LogDebug3);
if ((Err) || (ByteCount != BytesTransferred))
{
if (Err)
{
ss.str("");
ss << "ProcessServerRequests:read(2) error = " << Err.message() << "\n. Terminating.\n\n";
}
else
{
ss.str("");
ss << "ProcessServerRequests:read(3) error - BytesTransferred (" << Logger::NumberToString(BytesTransferred).c_str() <<
") != ByteCount (" << Logger::NumberToString(ByteCount).c_str() << "). Terminating.\n\n";
}
Log.LogString(ss.str(), LogError);
Terminate();
// Notify the client that an error has been encountered and the program needs to shut down. TBD.
break;
}
// Call the C# callback method that will handle the message.
Log.LogString("SSLSocket::ProcessServerRequests: sending msg to the C# client.\n\n", LogDebug2);
CallbackFunction(this, BytesTransferred, (void*)pReqBuf);
}
} while (ReqAlive);
Log.LogString("SSLSocket::ProcessServerRequests: worker thread done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::ProcessServerRequests: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
次のメソッドは、サーバーへのメッセージの送信を処理するためにcreate_threadメソッドによって呼び出されます。
void SSLSocket::SendWorkerThread()
{
// This method handles sending msgs to the server. It is called upon 1st time class initialization.
//
DWORD WaitResult;
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " started.\n", LogInfo);
// Loop until the user quits, or an error of some sort is thrown.
try
{
do
{
// If there are one or more msgs that need to be sent to a server, then send them out.
if (SendMsgQ.Count() > 0)
{
Message* pMsg = SendMsgQ.Pop();
// Byte* pBuf = pMsg->pBuf;
const Byte* pBuf = pMsg->pBuf;
SSLSocket* pSSL = pMsg->pSSL;
int BytesInMsg = pMsg->BytesInMsg;
boost::system::error_code Error;
unsigned int BytesTransferred = boost::asio::write(*pSSL->pSocket, boost::asio::buffer(pBuf, BytesInMsg), Error);
string s = "SSLSocket::SendWorkerThread: # bytes sent = ";
s += Logger::NumberToString(BytesInMsg).c_str();
s += "\n";
Log.LogString(s, LogDebug2);
Log.LogBuf(pBuf, BytesInMsg, DisplayInHex, LogDebug3);
if (Error)
{
Log.LogString("SSLSocket::SendWorkerThread: error sending message - " + Error.message() + "\n", LogError);
}
}
else
{
// Nothing to send, so go into a wait state.
WaitResult = WaitForSingleObject(hEvent, INFINITE);
if (WaitResult != 0L)
{
Log.LogString("SSLSocket::SendWorkerThread: WaitForSingleObject event error. Code = " + Logger::NumberToString(GetLastError()) + ". \n", LogError);
}
}
} while (ReqAlive);
Log.LogString("SSLSocket::SendWorkerThread: Worker thread " + Logger::NumberToString(boost::this_thread::get_id()) + " done.\n", LogInfo);
}
catch (std::exception& e)
{
stringstream ss;
ss << "SSLSocket::SendWorkerThread: threw an error - " << e.what() << ".\n";
Log.LogString(ss.str(), LogError);
}
}
したがって、同期読み取りが別のスレッドで保留されている間に同期書き込みを実行できる必要がある場合、誰かが私のコードが間違っていることを教えてもらえますか?