Invoke The stream does not support reading - c#

I have a c# network application where alot of anonymous users connect to (game service).
Now I check the logs and occasionally I see this exception:
[10:30:18.21352] System.Int32 Read(Byte[], Int32, Int32): The stream does not support reading.
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at BusinessLayer.Listener.ListenerWorker.ProcessClient(Object obj) in File.cs:line 141
This error comes from a NetworkStream object, now I am trying to reproduce the problem, but how? How can I get this exception?
I tried disconnecting myself, but that just gives a timeout, tried other things, but cannot get it to work.
Maybe somebody has an idea?
Contents of the file is:
private static void ProcessClient(
Object obj)
{
ISession session = (ISession)obj;
NetworkStream networkStream = null;
try
{
DebugUtility.SetThreadName("Worker: {0}", session.Name);
networkStream = session.TcpClient.GetStream();
networkStream.ReadTimeout = Config.ReadTimeout;
// Loop received packets (blocks untill next packet)
Int32 packetSize;
Byte[] buffer = new Byte[session.PacketSize];
while ((packetSize = networkStream.Read(buffer, 0, buffer.Length)) != 0)
{
// Get String from packet bytes
String packet = Encoding.UTF8.GetString(buffer, 0, packetSize);
// Check if packet has data
if (String.IsNullOrEmpty(packet))
continue;
// Log biggest received package
DebugUtility.CheckMaxPacketSize(session.Name, packet.Length);
// Handle packet (in new thread)
Logger.DebugLog("Received: {0}", packet);
ThreadPool.QueueUserWorkItem(session.HandlePacket, packet);
}
}
catch (Exception ex)
{
Logger.LogException(ex);
}
finally
{
if (networkStream != null)
networkStream.Close();
if (session != null)
session.Disconnect();
}
}

What arguments are you passing in the
System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
method. Are you using any of NetworkStream.Length or NetworkStream.Position properties.
i.e is it somthing like (not exactly)
System.Net.Sockets.NetworkStream.Read(buffer, stream.Position, stream.Length)
then as explained in MSDN documentation use of NetworkStream.Length and NetworkStream.Position properties will always throw a NotSupportedException as its not currently Supported.

Related

TcpListener and NetworkStream doesn't get data

I am trying to receive a message from an equipment. This equipment is an authentication terminal, and it will send the message as soon as the user set his credentials.
Also, the manual of the equipment says the message will be sent in the ILV format, standing I for identification, L for length and V for value.
a normal message would be:
I -> 0x00 (byte 0 indicating success)
L -> 0x04 0x00 (two bytes for length, being 4 the length in this case)
V -> 0x35 0x32 0x38 0x36 (the message itself)
The message is sent in TCP protocol, so I created a socket using the TcpListener class, following this sample from Microsoft:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx
new Thread(() =>
{
TcpListener server = null;
try
{
Int32 port = 11020;
IPAddress localAddr = IPAddress.Parse("192.168.2.2");
server = new TcpListener(localAddr, port);
server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
server.Start();
byte[] bytes = new byte[256];
String data = null;
while (true)
{
TcpClient client = server.AcceptTcpClient();
data = null;
NetworkStream stream = client.GetStream();
int i = 0;
while((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// this code is never reached as the stream.Read above runs for a while and receive nothing
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
client.Close();
}
}
catch (SocketException ex)
{
// Actions for exceptions.
}
finally
{
server.Stop();
}
}).Start();
If the stream.Read is removed, then the code flows (although I got nothing either), however if I put any stream.Read statement the execution holds for a while like it was waiting for some response, and then it ends with no response, all bytes read is zero.
I am running Wireshark on the computer and the data is being sent.
Anybody knows what I am doing wrong?
I think the problem is right in the Read method, which halts until the buffer fills completely, but that's obviously won't happen anytime.
In that while-loop, you should spin by checking for available data first, then read them and append to the byte-array. Moreover, since the loop become too "tight", it's better to yield the control to the task scheduler for a bit.
Here is an example:
while (true)
{
if (stream.DataAvailable)
{
int count = stream.Read(bytes, i, bytes.Length);
i += count;
// ...add something to detect the end of message
}
else
{
Thread.Sleep(0);
}
}
It's worthwhile to notice that there's no timeout, but that's a very common feature in order to quit the loop when no data (or broken) are incoming.
Furthermore, the above pattern is not the best way to accumulate data, because you may get an exception when too many bytes are received. Consider a spurious stream of 300 bytes: that will overflow the available buffer. Either use the "bytes" as a temporary buffer for what the Read method gives, or provide a safety check before calling the Read method (so that you may provide the best byte count to read).
Useful links here:
https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.networkstream.read?view=netframework-4.7.1#System_Net_Sockets_NetworkStream_Read_System_Byte___System_Int32_System_Int32_

How to (repeatedly) read from .NET SslStream with a timeout?

I just need to read up to N bytes from a SslStream but if no byte has been received before a timeout, cancel, while leaving the stream in a valid state in order to try again later. (*)
This can be done easily for non-SSL streams i.e. NetworkStream simply by using its ReadTimeout property which will make the stream throw an exception on timeout. Unfortunately this approach doesn't work on SslStream per the official docs:
SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.
[Updated 1] I tried a different approach like this:
task = stream->ReadAsync(buffer, 0, buffer->Length);
if (task->Wait(timeout_ms)) {
count = task->Result;
...
}
But this doesn't work if Wait() returned false: when calling ReadAsync() again later it throws an exception:
Exception thrown: 'System.NotSupportedException' in System.dll
Tests.exe Warning: 0 : Failed reading from socket: System.NotSupportedException: The BeginRead method cannot be called when another read operation is pending.
[Update 2] I tried yet another approach to implement timeouts by calling Poll(timeout, ...READ) on the underlying TcpClient socket: if it returns true, then call Read() on the SSlStream, or if it returns false then we have a timeout. This doesn't work either: because SslStream presumably uses its own internal intermediary buffers, Poll() can return false even if there's data left to be read in the SslStream.
[Update 3] Another possibility would be to write a custom Stream subclass that would sit between NetworkStream and SslStream and capture the timeout exception and return 0 bytes instead to SslStream. I'm not sure how to do this, and more importantly, I have no idea if returning a 0 bytes read to SslStream would still not corrupt it somehow.
(*) The reason I'm trying to do this is that reading synchronously with a timeout from a non-secure or secure socket is the pattern I'm already using on iOS, OS X, Linux and Android for some cross-platform code. It works for non-secure sockets in .NET so the only case remaining is SslStream.
You can certainly make approach #1 work. You simply need to keep track of the Task and continue waiting without calling ReadAsync again. So, very roughly:
private Task readTask; // class level variable
...
if (readTask == null) readTask = stream->ReadAsync(buffer, 0, buffer->Length);
if (task->Wait(timeout_ms)) {
try {
count = task->Result;
...
}
finally {
task = null;
}
}
Needs to be fleshed-out a bit so the caller can see that the read isn't completed yet but the snippet is too small to give concrete advice.
I also encountered this problem with an SslStream returning five bytes of garbage data on the read after a timeout, and I separately came up a solution that is similar to OP's Update #3.
I created a wrapper class which wraps the Tcp NetworkStream object as it is passed into the SslStream constructor. The wrapper class passes all calls onto to the underlying NetworkStream except that the Read() method includes an extra try...catch to suppress the Timeout exception and return 0 bytes instead.
SslStream works correctly in this instance, including raising the appropriate IOException if the socket is closed. Note that our Stream returning 0 from a Read() is different from a TcpClient or Socket returning 0 from a Read() (which typically means a socket disconnect).
class SocketTimeoutSuppressedStream : Stream
{
NetworkStream mStream;
public SocketTimeoutSuppressedStream(NetworkStream pStream)
{
mStream = pStream;
}
public override int Read(byte[] buffer, int offset, int count)
{
try
{
return mStream.Read(buffer, offset, count);
}
catch (IOException lException)
{
SocketException lInnerException = lException.InnerException as SocketException;
if (lInnerException != null && lInnerException.SocketErrorCode == SocketError.TimedOut)
{
// Normally, a simple TimeOut on the read will cause SslStream to flip its lid
// However, if we suppress the IOException and just return 0 bytes read, this is ok.
// Note that this is not a "Socket.Read() returning 0 means the socket closed",
// this is a "Stream.Read() returning 0 means that no data is available"
return 0;
}
throw;
}
}
public override bool CanRead => mStream.CanRead;
public override bool CanSeek => mStream.CanSeek;
public override bool CanTimeout => mStream.CanTimeout;
public override bool CanWrite => mStream.CanWrite;
public virtual bool DataAvailable => mStream.DataAvailable;
public override long Length => mStream.Length;
public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) => mStream.BeginRead(buffer, offset, size, callback, state);
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) => mStream.BeginWrite(buffer, offset, size, callback, state);
public void Close(int timeout) => mStream.Close(timeout);
public override int EndRead(IAsyncResult asyncResult) => mStream.EndRead(asyncResult);
public override void EndWrite(IAsyncResult asyncResult) => mStream.EndWrite(asyncResult);
public override void Flush() => mStream.Flush();
public override Task FlushAsync(CancellationToken cancellationToken) => mStream.FlushAsync(cancellationToken);
public override long Seek(long offset, SeekOrigin origin) => mStream.Seek(offset, origin);
public override void SetLength(long value) => mStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => mStream.Write(buffer, offset, count);
public override long Position
{
get { return mStream.Position; }
set { mStream.Position = value; }
}
public override int ReadTimeout
{
get { return mStream.ReadTimeout; }
set { mStream.ReadTimeout = value; }
}
public override int WriteTimeout
{
get { return mStream.WriteTimeout; }
set { mStream.WriteTimeout = value; }
}
}
This can then be used by wrapping the TcpClient NetworkStream object before it's passed to the SslStream, as follows:
NetworkStream lTcpStream = lTcpClient.GetStream();
SocketTimeoutSuppressedStream lSuppressedStream = new SocketTimeoutSuppressedStream(lTcpStream);
using (lSslStream = new SslStream(lSuppressedStream, true, ServerCertificateValidation, SelectLocalCertificate, EncryptionPolicy.RequireEncryption))
The problem comes down to SslStream corrupting its internal state on any exception from the underlying stream, even a harmless timeout. Oddly, the five (or so) bytes of data that the next read() returns are actually the start of the TLS encrypted payload data from the wire.
Hope this helps

SSLStream reads invalid data + KB3147458 SSLStream bug (?)

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.
The problem was that I re-used the SSLStream after a timeout. So I solved the problem just by removing the nRetry variable and set a longer timeout. The related MSDN article says that SSLStream will return garbage after a timeout exception (https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx):
SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.
Another issue is that Windows update KB3147458 (Windows 10 April's update) changes the something in the behaviour of the Read function. It looks like something in the SSLStream implementation changed and now it returns data in 2 parts, 1 byte and the rest of the bytes every single time. Actually the MSDN document doesn't say that the Read() function will return all the requested bytes in one step and the provided example uses a do-while loop in order to read the exact number of bytes. So I suppose that the Read() function doesn't guarantee to read the exact requested number of bytes all at once, more read iterations might be required.
SSLstream works properly so it's NOT BROKEN. You just need to pay attention and use of a do-while loop and check that all the bytes are read correctly.
I changed the code as shown here to address the bugs I had.
public String ReadMessage()
{
if (tcpClient != null)
{
int bytes = -1, offset = 0;
byte[] buffer = new byte[MESSAGE_SIZE];
try
{
// perform multiple read iterations
// and check the number of bytes received
while (offset < MESSAGE_SIZE)
{
bytes = sslStream.Read(buffer, offset, MESSAGE_SIZE - offset);
offset += bytes;
if (bytes == 0)
{
return String.Empty;
}
}
}
catch (Exception)
{
// Some exception occured
return String.Empty;
}
if (offset != 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;
}
With regard to the SslStream returning five bytes on a Read() after a timeout, this is because the SslStream class doesn't gracefully handle any IOException from the underlying stream, and this is documented as previously noted.
SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.
https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx
However, you can fix the problem by creating a wrapper class that sits between the Tcp NetworkStream and the SslStream which catches and suppresses the harmless timeout exception, with (seemingly) no loss of functionality.
The full code of this is in my answer on a similar thread, here https://stackoverflow.com/a/48231248/8915494
With regard to the Read() method returning only part of the payload on each Read(), your answer already fixes this correctly. While this is "recent" behaviour for SslStream, it is unfortunately expected behaviour for all networking and all code needs to create some form of buffer to store the fragments until you have a complete packet. For example, if your data exceeds 1500 bytes (the maximum packet size for most Ethernet adapters, assuming Ethernet transport), you are very likely to receive the data in multiple parts and have to reassemble it yourself.
Hope this helps

how to implement stream readtimeout property

I'm fixing a bug : the app will hang in download process for 5 minutes while losing network connection. What I need to do is reduce the hanging time to like 20 seconds.
log snippet:
Severe 2016-01-27 , 11:03:14
在 HttpsDownload.doDownloadByHttps(FileDownloadInfo fileInfo, downloadingDelegate downDelegate)
Message: Init method:GET
Info 2016-01-27 , 11:08:29
在 HttpsDownload.writeToFile(FileDownloadInfo fileInfo, Stream stream, downloadingDelegate downDelegate, Boolean Clear)
Message: Exception while reading from the response stream
Exception:
Message: time out
Type: System.Net.WebException
在 System.Net.ConnectStream.Read(Byte[] buffer, Int32 offset, Int32 size)
在 HttpsDownload.writeToFile(FileDownloadInfo fileInfo, Stream stream, downloadingDelegate downDelegate, Boolean Clear)
code snippet:
private void doDownloadByHttps(FileDownloadInfo fileInfo, downloadingDelegate downDelegate)
{
//code
webResponse = (HttpWebResponse)webRequest.GetResponse();
responseStream = webResponse.GetResponseStream();
writeToFile(fileInfo, responseStream, downDelegate, bClear);
}
private void writeToFile(FileDownloadInfo fileInfo, Stream stream, downloadingDelegate downDelegate, bool Clear)
{
int count = stream.Read(readBuffer, 0, BlockSize);
while (count > 0)
{
//code
count = stream.Read(readBuffer, 0, BlockSize);
}
}
I believe that timeout occurs in stream.Read according to the log. So the easiest way to do the trick should be stream.ReadTimeout = 20000, but obviously I can't set the ReadTimeout property.
Another way that can think of is to write a class override ReadTimeout like:
public class MyStream : Stream {
//code
public override int ReadTimeout { get; set; }
//code
}
But it's not preferred. Any better way to fix the bug?
The timeout needs to be set in the webRequest object, of type HttpWebRequest.
Check this out: https://msdn.microsoft.com/en-US/library/system.net.httpwebrequest.readwritetimeout(v=vs.110).aspx

Properly Closing a FileStream after Async Timeout

I'm writing a C# application (limited to .NET framework 4.0 or lower (i.e. no Read/WriteAsync)) that is responsible for reading / writing files between a Windows (7 or later) machine and a NFS-mounted Unix-based server.
The client machines are mobile and are expected to be disconnected from the network periodically. Because of this, I wish to harden my application to gracefully handle disconnects in the middle of a read or write operation (hopefully by accident).
To keep track of whether a timeout has occurred, I asynchronously read (or write) and use the AsyncWaitHandle of the IAsyncResult returned by FileStream.BeginRead:
private int PerformRead (FileStream readStream,
byte [] buffer,
int nBytes)
{
IAsyncResult readResult = null;
try
{
readResult = readStream.BeginRead
(buffer, 0, nBytes, null, null);
// If the read was completed, return # of bytes read.
if (readResult.AsyncWaitHandle.WaitOne (1000))
{
return readStream.EndRead (readResult);
}
// Otherwise, the timeout has elapsed.
else
{
throw new TimeoutException ();
}
}
finally
{
if (null != readResult)
{
readResult.AsyncWaitHandle.Close ();
}
}
}
My issue arises with cleaning up my streams in the case of a time out. It appears that calling Dispose() or Close() on the stream hangs indefinitely while the connection is severed. To attempt to remedy this, I explicitly call Win32's CloseHandle() on the file handle on a timeout. This function appears to also block while the connection is severed. The following represents my treatment of the read stream.
private void ReadNewFile (string path,
byte [] buffer)
{
IntPtr readHandle = IntPtr.Zero;
FileStream readStream = null;
try
{
readHandle = CreateFile (path
0xc0000000,
0,
IntPtr.Zero,
3,
0x40000000,
IntPtr.Zero);
SafeFileHandle safeReadHandle = new SafeFileHandle (readHandle, true);
if (safeReadHandle.IsInvalid)
{
throw new Exception ();
}
readStream = new FileStream (safeReadHandle,
FileAccess.Read,
4092,
true);
while (true)
{
// Read part of the file into the buffer.
int bytesRead = PerformRead (readStream, buffer, buffer.Length);
// If EOF reached, break.
if (bytesRead == 0)
{
break;
}
}
}
catch (TimeoutException)
{
// NOTE: Hangs indefinitely if the network cable is unplugged.
CloseHandle (readHandle);
}
finally
{
if (null != readStream)
{
// NOTE: Hangs indefinitely if both of the following are true:
// - Network cable is unplugged (resulting in TimeoutException)
// - The body of the caught TimeoutException is commented out.
readStream.Close ();
}
}
}
Is there a recommended method of properly disposing of a FileStream for a file that is no longer available? Note that any call to BeginRead will also hang indefinitely if the connection has severed.

Categories

Resources