I'm having an issue with SSLStream returning some data when the remote client did not send anything. I am having this issue when the server is listening for a new command. If the server doesn't receive a new request, the ReadMessage() function should catch an IOException due to the Read timeout of the SSLStream. The problem happens when the sslStream.Read() is executed the second time it seems to read 5 bytes which were not sent by the client. So the problem happens in this sequence:
-> ReadMessage() -> sslstream.Read() -> timeout exception caught as expected
-> ReadMessage() -> sslstream.Read() -> timeout exception NOT caught, 5 bytes read even even though the client did not send anything
-> ReadMessage() -> sslstream.Read() -> timeout exception caught as expected
-> ReadMessage() -> sslstream.Read() -> timeout exception NOT caught, 5 bytes read even though client did not send anything...
and so on..
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;
}
The very same ReadMessage(), SendMessage() and CreateMessage() functions are used also by the client to send messages to the server. MESSAGE_SIZE constant is also the same and it's set to 2048.