リモートクライアントが何も送信しなかったときにSSLStreamでデータを返す問題があります。サーバーが新しいコマンドをリッスンしているときにこの問題が発生しています。 サーバーが新しい要求を受け取らない場合、ReadStream()関数はSSLStreamの読み取りタイムアウトのためにIOExceptionをキャッチする必要があります。問題は、sslStream.Read()が2回目にクライアントによって送信されなかった5バイトを読み取ったように実行されたときに発生します。だから、問題は、この順序で起こる:SSLStreamで無効なデータが読み取られました+ KB3147458 SSLStreamのバグ(?)
- > ReadMessage() - - > sslstream.Read() - >タイムアウト> ReadMessage() - > sslstream.Read() - 予想
として捉え>タイムアウト例外キャッチされない例外は、クライアントが
何も送信しませんでしたにもかかわらずさえ読ん5バイト - > ReadMessage() - > sslstream.Read() - >期待通りにタイムアウト例外をキャッチ
- > ReadMessage() - >をsslstream.Read() - >クライアントが何も送信しなかったにもかかわらず、5バイトが読み取られなかったタイムアウト例外...
など..
public void ClientHandle(object obj)
{
nRetry = MAX_RETRIES;
// Open connection with the client
if (Open() == OPEN_SUCCESS)
{
String request = ReadMessage();
String response = null;
// while loop for the incoming commands from client
while (!String.IsNullOrEmpty(request))
{
Console.WriteLine("[{0}] {1}", RemoteIPAddress, request);
response = Execute(request);
// If QUIT was received, close the connection with the client
if (response.Equals(QUIT_RESPONSE))
{
// Closing connection
Console.WriteLine("[{0}] {1}", RemoteIPAddress, response);
// Send QUIT_RESPONSE then return and close this thread
SendMessage(response);
break;
}
// If another command was received, send the response to the client
if (!response.StartsWith("TIMEOUT"))
{
// Reset nRetry
nRetry = MAX_RETRIES;
if (!SendMessage(response))
{
// Couldn't send message
Close();
break;
}
}
// Wait for new input request from client
request = ReadMessage();
// If nothing was received, SslStream timeout occurred
if (String.IsNullOrEmpty(request))
{
request = "TIMEOUT";
nRetry--;
if (nRetry == 0)
{
// Close everything
Console.WriteLine("Client is unreachable. Closing client connection.");
Close();
break;
}
else
{
continue;
}
}
}
Console.WriteLine("Stopped");
}
}
public String ReadMessage()
{
if (tcpClient != null)
{
int bytes = -1;
byte[] buffer = new byte[MESSAGE_SIZE];
try
{
bytes = sslStream.Read(buffer, 0, MESSAGE_SIZE);
}
catch (ObjectDisposedException)
{
// Streams were disposed
return String.Empty;
}
catch (IOException)
{
return String.Empty;
}
catch (Exception)
{
// Some other exception occured
return String.Empty;
}
if (bytes != MESSAGE_SIZE)
{
return String.Empty;
}
// Return string read from the stream
return Encoding.Unicode.GetString(buffer, 0, MESSAGE_SIZE).Replace("\0", String.Empty);
}
return String.Empty;
}
public bool SendMessage(String message)
{
if (tcpClient != null)
{
byte[] data = CreateMessage(message);
try
{
// Write command message to the stream and send it
sslStream.Write(data, 0, MESSAGE_SIZE);
sslStream.Flush();
}
catch (ObjectDisposedException)
{
// Streamers were disposed
return false;
}
catch (IOException)
{
// Error while trying to access streams or connection timedout
return false;
}
catch (Exception)
{
return false;
}
// Data sent successfully
return true;
}
return false;
}
private byte[] CreateMessage(String message)
{
byte[] data = new byte[MESSAGE_SIZE];
byte[] messageBytes = Encoding.Unicode.GetBytes(message);
// Can't exceed MESSAGE_SIZE parameter (max message size in bytes)
if (messageBytes.Length >= MESSAGE_SIZE)
{
throw new ArgumentOutOfRangeException("message", String.Format("Message string can't be longer than {0} bytes", MESSAGE_SIZE));
}
for (int i = 0; i < messageBytes.Length; i++)
{
data[i] = messageBytes[i];
}
for (int i = messageBytes.Length; i < MESSAGE_SIZE; i++)
{
data[i] = messageBytes[messageBytes.Length - 1];
}
return data;
}
非常に同じReadMessage()、のSendMessage()とCreateMessage()関数は、サーバーにメッセージを送信するためにクライアントによっても使用されています。 MESSAGE_SIZE定数も同じで、2048に設定されています。
クリスタルボールは、あなたがMESSAGE_SIZEを変更し、サーバが使用するDLLのコピーを更新するのを忘れていることを言うことができます。コードに基本的なバグがあります.SSlStreamのMSDN記事のコードサンプルを見て、ReadMessage()メソッドのdo-whileループに注意してください。あなたには欠けています。 –
MESSAGE_SIZE定数は同じで、2048に設定されています。問題は、sslStreamを再利用していることがわかりました。 MSDNの記事によると、SSLStreamはタイムアウト例外が発生した後にgargabeを返します。 –
はい、ReadMessage()は、すべてのバイトが正しく読み取られることを確認するdo-whileループを必要とします。 –