Why is NetworkStream.ReadAsync not timing out? - c#

I am using NetworkStream with TcpClient.
First I setup my tcp client:
tcp = new TcpClient(AddressFamily.InterNetwork)
{ NoDelay = true, ReceiveTimeout = 5000};
My main data-receiving loop:
while (true)
{
//read available data from the device
int numBytesRead = await ReadAsync();
Console.WriteLine($"{numBytesRead} bytes read"); //BP2
}
And the actual TCP data reading:
public Task<int> ReadAsync()
{
var stream = tcp.GetStream();
return stream.ReadAsync(InBuffer, 0, InBuffer.Length); //BP1
}
I have this connected to a testbed which lets me send manual packets. Through setting breakpoints and debugging I have checked that stream.ReadTimeout takes the value 5000 from tcp.
If I send data frequently it all works as expected. But if I don't send any data, nothing appears to happen after 5s, no timeout. I see breakpoint BP1 being hit in the debugger but until I send data from my testbed, BP2 is not hit. I can leave it a minute or more and it just seems to sit waiting, but receives data sent after a minute, which appears to be incorrect behavior. After 5 seconds something should happen, surely (an exception as I understand it)?
It's late so I am expecting something really basic but can anyone see what my mistake is and a resolution?
Addendum
OK so when I RTFM for the actual .Net version I'm using (how may times have I been caught out by MS defaulting to .Net Core 3, I did say it was late) I see in the remarks sectio for ReadTimeout:
This property affects only synchronous reads performed by calling the
Read method. This property does not affect asynchronous reads
performed by calling the BeginRead method.
I'm unclear now if I can use modern awaitable calls at all to read socket data safely and with a timeout specifically. It's working except for the timeout but I'm not sure how given ReadAsync has no override in NetworkStream. Must I do some ugly hack or is there a simple solution?
In my case 5000 is the longest I can expect not to receive data before concluding there is a problem - the protocol has no ping mechanism so if nothing appears I assume the connection is dead. Hence thinking an Async read with a 5000ms timeout would be nice and neat.

Timeout values for network objects apply only to synchronous operations. For example, from the documentation:
This option applies to synchronous Receive calls only.
For Socket.ReceiveTimeout, TcpClient.ReceiveTimeout, and NetworkStream.ReadTimeout, the implementations all ultimately result in a call to SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ...) which in turn is effectively calling the native setsockopt() function. From that documentation:
SO_RCVTIMEO DWORD Sets the timeout, in milliseconds, for blocking receive calls.
(emphasis mine)
It's this limitation in the underlying native API that is the reason for the same limitation in the managed API. Timeout values will not apply to asynchronous I/O on the network objects.
You will need to implement the timeout yourself, by closing the socket if and when the timeout should occur. For example:
async Task<int> ReadAsync(TcpClient client, byte[] buffer, int index, int length, TimeSpan timeout)
{
Task<int> result = client.GetStream().ReadAsync(buffer, index, length);
await Task.WhenAny(result, Task.Delay(timeout));
if (!result.IsCompleted)
{
client.Close();
}
return await result;
}
Other variations on this theme can be found in other related questions:
NetworkStream.ReadAsync with a cancellation token never cancels
Cancel C# 4.5 TcpClient ReadAsync by timeout
Closing the socket is really all that you can do. Even for synchronous operations, if a timeout occurs the socket would no longer be usable. There is no reliable way to interrupt a read operation and expect the socket to remain consistent.
Of course, you do have the option of prompting the user before closing the socket. However, if you were to do that, you would implement the timeout at a higher level in your application architecture, such that the I/O operations themselves have no awareness of timeouts at all.

Related

Task not cancelled after a specified timeout

I'm using a StreamSocketListener to await a connection on a port. I don't want it to listen forever, it must cancel after a specific number of seconds, for which I'm using the following code.
remoteListener = new StreamSocketListener();
try
{
CancellationTokenSource ctsTimeout = new CancellationTokenSource();
ctsTimeout.CancelAfter(1000); // in milliseconds
await remoteListener.BindServiceNameAsync(receivingPortForRemoteRequests.ToString()).AsTask(ctsTimeout.Token);
remoteListener.ConnectionReceived += remoteListener_ConnectionReceived;
}
catch (Exception exc) // supposed to produce a TaskCanceledException
{
isCancelled = true;
}
My problem is that this code never throws the Exception after any interval of time, but just keeps listening. The code is based on what I'd found from this MSDN page.
Does anyone know what I'm doing wrong ? Thanks in advance!
I would say that the primary mistake you have made is that you are passing the cancellation token to the task that binds the socket, and not any operation that actually listens. The binding operation simply assigns a port to the socket, and generally will complete within milliseconds at the worst, quite likely faster in the typical case. There's no way this operation would ever still be in progress after a full second.
Without a good Minimal, Complete, and Verifiable example that clearly illustrates your question, it's impossible to provide a thorough answer. However, some suggestions:
First, don't bother using the cancellation token. It's not how you should stop a socket from listening. Instead, just close the socket after the requisite time. You can use a timer for this purpose, or an async method that first calls await Task.Delay(...) and then closes the socket.
In the future, if you do have a scenario where using a cancellation token is appropriate, you should catch only TaskCanceledException. Never use catch (Exception) for routine exception handling; the only place it's appropriate is for scenarios where you intend to simply log or otherwise report the exception and then terminate the process. Otherwise, catch only the exceptions you expect and for which you have a good plan for handling.
You should subscribe to the ConnectionReceived event before you bind the socket. Otherwise, there is a chance (very small, granted) that a connection attempt would be made before your code is ready to be notified via the event.
The first and third points above are addressed in the MSDN documentation, which has a useful summary of the proper use of this class. From the documentation for StreamSocketListener:
The typical order of operations is as follows:
• Create the StreamSocketListener.
• Use the Control property to retrieve a StreamSocketListenerControl object and set the socket quality of service required.
• Assign the ConnectionReceived event to an event handler.
• Call the BindServiceNameAsync or BindEndpointAsync method to bind to a local TCP port number or service name. For Bluetooth RFCOMM, the local service name parameter is the Bluetooth Service ID.
• When a connection is received, use the StreamSocketListenerConnectionReceivedEventArgs object to retrieve the Socket property with the StreamSocket object created.
• Use the StreamSocket object to send and receive data.
• Call the Close method to stop listening for and accepting incoming network connections and release all unmanaged resources associated with the StreamSocketListener object. Any StreamSocket objects created when a connection is received are unaffected and can continue to be used as needed.

Thread doesn't continue upon resume computer

I connect to a client using TcpClient.BeginConnect where i have a loop that is running as long as a CancellationToken isn't requested cancelled.
while (!_token.IsCancellationRequested)
{
var s = reader.ReadLine() ?? string.Empty;
}
I also reconnect and handle exceptions with logging.
When I start this application everything works as expected. However, when I put my computer to sleep and wake it up again it seems all the threads have terminated.
The workflow is as follows:
I start (from the main thread) a new Task which executes DoWork. I execute like this: Task.Run(()=>DoWork(),_token);
DoWork instantiates a new TcpClient and initiates a BeginConnect like this:
(_client = new TcpClient()).BeginConnect(Address, Port, ConnectCallback, _client);
Within ConnectCallback I have a while statement which keeps reading data from the stream (see above).
Any idea what happens to the threads when the computer goes to sleep?
When your computer wakes from a sleep, the connections will sometimes (but not always) be severed. It depends on a number of factors, some of which might not be under your control.
In terms of your callback method, there is no exception triggered but reader.EndOfStream will be true when the connection has ended and all prior data has been read. Edit: However, if the TCP stack is unaware that the remote has disconnected, this call will block until data arrives, the TcpClient.ReceiveTimeout period has passed, or until the TCP session idle timeout, whichever happens first.
(Edit: If no data is sent, the connection will only end automatically if the TCP stack is aware (based on its own network state detection, or TCP packets) that the remote has disconnected; otherwise it will wait until session idle timeout, which can take up to an hour or so, depending on the client. One solution is for the client to send data regularly (if it has not received data recently) to act as a kind of early disconnection detection.)
In fact when the connection ends (e.g. after your computer wakes up), the while loop you posted would go into a tight loop, maxing out a CPU core, because reader.ReadLine() keeps returning null. You can check for that and break out of the loop:
if (reader.EndOfStream)
{
break;
}
The solution to this problem (although not a nice one) was to use a timer which explicitly closed the connection after a given amount of time.
var timeoutTimer = new Timer(state =>
{
var temp = (TcpClient)state;
Log.Error("Read timeout. Closing connection.");
temp.Close();
}, client, Timeout.Infinite, Timeout.Infinite);
Prior to accessing the stream I activate this timer:
timeoutTimer.Change(20000, Timeout.Infinite); // Handle timeouts.
And reset it afterwards:
timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); // Reset timeout.
Both using reader.EndOfStream or reader.ReadLine causes the thread to stop at that point, which isn't resolved unless the connetion is forcefully terminated.
EDIT:
Setting TcpClient.ReceiveTimeout does the same thing as above - probably better. Throws an IOException when the reciever does not receive any data for the specified amount of time (in ms).

.NET 4.5 SslStream - Cancel a asynchronous read/write call?

Is there any way to cancel a asynchronous read or write task on a SslStream? I have tried providing ReadAsync with a CancellationToken but it doesnt appear to work. When the following code reaches it's timeout (the Task.Delay), it calls cancel on the CancellationTokenSource which should cancel the read task, returns a error to the calling method, and the calling method eventually tries to read again, which raises a "The BeginRead method cannot be called when another write operation is pending" exception.
In my specific application I could work around this by closeing the socket and reconnecting, but there is a high overhead associated with reestablishing the connection so it is less than ideal.
private async Task<int> ReadAsync(byte[] buffer, int offset, int count, DateTime timeout)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
if (socket.Poll(Convert.ToInt32(timeout.RemainingTimeout().TotalMilliseconds) * 1000, SelectMode.SelectRead) == true)
{
Task<int> readTask = stream.ReadAsync(buffer, offset, count, cancellationTokenSource.Token);
if (await Task.WhenAny(readTask, Task.Delay(timeout.RemainingTimeout())) == readTask)
return readTask.Result;
else
cancellationTokenSource.Cancel();
}
return -1;
}
Looking at the doc for SslStream, it does not support ReadAsync (it simply uses the fallback synchronous implementation from Stream). Since SslStream is a decorator Stream, is isn't obvious how to safely recover from a timeout on the underlying Stream, and the only obvious way would be to re-initialize the entire Stream pipeline. However given that the underlying stream might not be seekable, again this might not be idea.
For support of cancellation, the stream would have to override Stream.ReadAsync(Byte[], Int32, Int32, CancellationToken). In the documentation, neither NetworkStream nor SslStream overrides the overload of ReadAsync required to consume cancellation (and abstract Stream couldn't possibly implement generic cancellation). For an example where cancellation IS supported, see FileStream and contrast how the documentation differs.
So for a concrete case, if we were decorating HttpStream using SslStream then after a timeout we would want to recover by opening the HttpStream back at the position where we timed out (using the Range header). But there is no way to expose that generically using the IO.Stream class.
Finally you should consider what your failure case should be. Why would ReadAsync timeout? In the majority of cases I can think of, it should be due to unrecoverable network issues, which would necessitate the Stream being reinitialized.
Bonus point. Have you considered refactoring out your Timeout behaviour into a decorator Stream? You could then place the timeout decorator onto your underlying stream.
stream = new SslStream(
new TimeoutStream(new FooStream(), Timespan.FromMilliseconds(1000)));

When does Socket.ReceiveAsync return synchronously?

I tried to compare the performance between synchronous and asynchronous methods when reading small amounts of data that had already been received.
So I send the data and make sure it is waiting on the receiving socket(same program).
source.Send(sendBuffer);
while (target.Available != sendBuffer.Length)
Thread.Sleep(50);
SocketAsyncEventArgs arg = new SocketAsyncEventArgs();
arg.Completed += AsyncCallback;
arg.SetBuffer(receiveBuffer, 0, sendBuffer.Length);
if(target.ReceiveAsync (arg))
{
//Pending request
//999 out of 1000 times we end up here
}
To my surprise the the ReceiveAsync call almost never finished synchronously.
I would assume that a ReceiveAsync would return synchronously and perform as fast as Receive but since it almost never happen I can't tell.
Were my expectations of ReceiveAsync wrong here?
Would it be better to always first check target.Available and do a Receive if there is enough bytes available?
This kind of breaks some of the benefits with using ReceiveAsync rather than BeginReceive, that is why I'm writing this question.
ReceiveAsync just calls into the underlying Winsock subsystem. ReceiveAsync uses what is called Overlapped IO (asynchronous IO). In the case of a socket created to use overlapped IO: "Regardless of whether or not the incoming data fills all the buffers, the completion indication occurs for overlapped sockets". Where "completion indication" is the callback that will be called asynchronously. Which means ReceiveAsync will be asynchronous all the time (unless there's an error).
Sorry for late reply :)
i fixed my problem using Socket.Available Property.
Do
If MySocket.Available > 0 Then
bytes = MySocket.Receive(bytesReceived, bytesReceived.Length, 0)
textFrom &= Encoding.ASCII.GetString(bytesReceived, 0, bytes)
Else
Exit Do
End If
Loop While bytes = bytesReceived.Length

C# Socket.BeginSend Sometimes Seems to Behave Synchronously?

While attempting to send a message for a queue through the BeginSend call seem te behave as a blocking call.
Specificly I have:
public void Send(MyMessage message)
{
lock(SEND_LOCK){
var state = ...
try {
log.Info("Begin Sending...");
socket.BeginSend(message.AsBytes(),0, message.ByteLength, SocketFlags.None,
(r) => EndSend(r), state);
log.Info("Begin Send Complete.");
}
catch (SocketException e) {
...
}
}
}
The callback would be something like this:
private void EndSend(IAsyncResult result) {
log.Info("EndSend: Ending send.");
var state = (MySendState) result.AsyncState;
...
state.Socket.EndSend(result, out code);
log.Info("EndSend: Send ended.");
WaitUntilNewMessageInQueue();
SendNextMessage();
}
Most of the time this works fine, but sometimes it hangs. Logging indicates this happens when BeginSend en EndSend are excecuted on the same Thread. The WaitUntilNewMessageInQueue blocks until there is a new message in the queue, so when there is no new message it can wait quit a while.
As far as I can tell this should not really be a problem, but in the some cases BeginSend blocks causing a deadlock situation where EndSend is blocking on WaitUntilNewMessageInQueue (expected), but Send is blocking on BeginSend in return as it seems te be waiting for the EndSend callback te return (not expected).
This behaviour was not what I was expecting. Why does BeginSend sometimes block if the callback does not return in timely fashion?
First of all, why are you locking in your Send method? The lock will be released before the send is complete since you are using BeginSend. The result is that multiple sends can be executing at the same time.
Secondly, do not write (r) => EndSend(r), just write EndSend (without any parameters).
Thrid: You do not need to include the socket in your state. Your EndSend method is working like any other instance method. You can therefore access the socket field directly.
As for your deadlocks, it's hard to tell. You delegate may have something to do with it (optimizations by the compiler / runner). But I have no knowledge in that area.
Need more help? Post more code. but I suggest that you fix the issues above (all four of them) and try again first.
Which operating system are you running on?
Are you sure you're seeing what you think you're seeing?
The notes on the MSDN page say that Send() CAN block if there's no OS buffer space to initiate your async send unless you have put the socket in non blocking mode. Could that be the case? Are you potentially sending data very quickly and filling the TCP window to the peer? If you break into the debugger what does the call stack show?
The rest is speculation based on my understanding of the underlying native technologies involved...
The notes for Send() are likely wrong about I/O being cancelled if the thread exits, this almost certainly depends on the underlying OS as it's a low level IO Completion Port/overlapped I/O issue that changed with Windows Vista (see here: http://www.lenholgate.com/blog/2008/02/major-vista-overlapped-io-change.html) and given that they're wrong about that then they could be wrong about how the completions (calls to EndSend() are dispatched on later operating systems). From Vista onwards it's possible that the completions could be dispatched on the issuing thread if the .Net sockets wrapper is enabling the correct options on the socket (see here where I talk about FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)... However, if this were the case then it's likely that you'd see this behaviour a lot as initially most sends are likely to complete 'in line' and so you'd see most completions happening on the same thread - I'm pretty sure that this is NOT the case and that .Net does NOT enable this option without asking...
This is how you check if it completed synchronously so you avoid the callback on another thread.
For a single send:
var result = socket.BeginSend(...);
if (result.CompletedSynchronously)
{
socket.EndSend(result);
}
For a queue of multiple sends, you can just loop and finalize all synchronous sends:
while (true)
{
var result = socket.BeginSend(...);
if (!result.CompletedSynchronously)
{
break;
}
socket.EndSend(result);
}

Categories

Resources