Calling BeginRead from a NetworkStream - c#

Ok I want to connect to a Socket and read a network stream using the System.Net.Sockets.NetworkStream class. This is what I have so far:
NetworkStream myNetworkStream;
Socket socket;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.IPv4);
socket.Connect(IPAddress.Parse("8.8.8.8"), 8888);
myNetworkStream = new NetworkStream(socket);
byte[] buffer = new byte[1024];
int offset = 0;
int count = 1024;
myNetworkStream.BeginRead(buffer, offset, count, ??, ??);
Now I need an AsyncCallback and an Object state to complete my BeginRead method but I'm not even sure if this is going to work. I'm a bit lost at this point! Where do I need to go from here?

Basically, when you call the Begin* method on an asynchronous operation, there needs to be a call to a corresponding End* statement (for more detailed information, see the Asynchronous Programming Overview on MSDN, specifically the section titled "Ending an Asynchronous Operation").
That said, you generally want to pass a method/anonymous method/lambda expression which will do one or two things:
1) Call the corresponding End* method, in this case, Stream.EndRead. This call will not block when called because the callback will not be called until the operation is complete (note that if an exception occurred during the async call then this exception will be thrown when the End* method is called).
2) Possibly start a new asynchronous call. In this case, it's assumed you'll want to read more data, so you should start a new call to Stream.BeginRead
Assuming you want to do #2, you can do the following:
// Declare the callback. Need to do that so that
// the closure captures it.
AsyncCallback callback = null;
// Assign the callback.
callback = ar => {
// Call EndRead.
int bytesRead = myNetworkStream.EndRead(ar);
// Process the bytes here.
// Determine if you want to read again. If not, return.
if (!notMoreToRead) return;
// Read again. This callback will be called again.
myNetworkStream.BeginRead(buffer, offset, count, callback, null);
};
// Trigger the initial read.
myNetworkStream.BeginRead(buffer, offset, count, callback, null);
However, if you are using .NET 4.0, this becomes much easier using the FromAsync method on the TaskFactory class.

Related

.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)));

IO Completion Ports (IOCP)

When I call BeginExecuteReader (SQL) does it use IO Completion Ports? What makes it to be asynchronous? Should I call it from Begin[Operation]() or from the wrapper for IAsyncResult constructor?
does it use IO Completion Ports
Maybe, but that's an implementation detail deep in the native SQL Client's implementation.
Remember the SQL Server ADO.NET calls into the (local) native SQL Server client which will perform communication with the server.
What makes it to be async?
Its implementation. There is more than one way to do this, from approaches which fake it (perform blocking operations with a dedicated worker thread) through to IO Completion Ports and other "true" asynchronous APIs.
Where from should I call it ?
Wherever is right for your design.
for IAsyncResult constructor?
How you use the asynchronous APIs (callback passed to BeginExecuteReader, poll the returned IAsyncResult's IsComplete, wait on IAsyncResult, convert to a Task with TaskFactory.FromAsync, using the Reactive Extensions with Observable.FromAsyncPattern, or something else) is again part of the design of your code.
There are too many possibilities to give any form of guidance.
Whether or not the method uses IO Completion Ports is up to the implementation.
The call is asynchronous if it returns before the work is completed. Obviously that's also up to the implementation.
The standard way to use it would be
{
...
var fs = new FileStream(...); // using FileStream as an example
var data = new byte[100];
var ar = fs.BeginRead(data, 0, 100, MyCallback, new object[] { fs, data });
...
// you can check the IAsyncResult ar for completion,
// or do some work that doesn't depend on the data
}
private void MyCallback(IAsyncResult ar)
{
// AsyncState gets the last parameter from the call to BeginRead
var fs = (FileStream)(((object[])ar.AsyncState)[0]);
var data = (FileStream)(((object[])ar.AsyncState)[1]);
int numberOfBytesRead = fs.EndRead(ar);
// do something with data
}
You could also use a lambda expression as for a callback of course. With lambdas the state doesn't have to be captured manually so you could use it like
var ar = fs.BeginRead(data, 0, 100, result =>
{
int numberOfBytesRead = fs.EndRead(result);
// do something with data
}, null);

When does a Stream EndRead Block the Callback

I am relatively new to using the Async pattern for stream reading and writing, and wondered if the answer to this question is so obvious it is not written anywhere explicitly:
When calling a NetworkStream.BeginRead, I pass a callback parameter, which according to MSDN is executed "when BeginRead completes". It also says that "your callback method should call the EndRead method."
Then according to the documentation for NetworkStream.EndRead, the "method completes the asynchronous read operation started in the BeginRead method." It also mentions that this method "blocks until data is available."
I know the EndRead method is also useful for determining the number of bytes received.
My question is:
If the EndRead method is called within the BeginRead callback, does it ever really block? Isn't the read operation already complete when the callback is invoked?
Sample Code
byte[] streamBuffer = new byte[1024];
public void SomeFunction()
{
TcpClient client = new TcpClient();
client.Connect(IPAddress.Parse("127.0.0.1"), 32000);
NetworkStream stream = client.GetStream();
stream.BeginRead(streamBuffer,0,streamBuffer.Length,ReadCallback,stream);
}
public void ReadCallback(IAsyncResult ar)
{
NetworkStream stream = ar.AsyncState as NetworkStream;
// Will this call ever actually "block" or will it
// return immediately? Isn't the read operation
// already complete?
int bytesRead = stream.EndRead(ar);
// Other stuff here
}
The read operation is always completed when the callback fires. Completion is what gets the callback fired in the first place. So EndRead() will never block when it is used in the callback method.
Do note that "completed" can also mean "failed", EndRead() will throw the exception. A very common exception is ObjectDisposedException, thrown when the socket is closed while an asynchronous read is in progress. Typical when you exit your program, be sure to catch it.
You can use EndRead in two situations:
You can use EndRead in your call back functions. At this point nothing will be blocked.
You can also use EndRead without the call back function:
myStream.BeginRead(...);
// Do a lot of work at the same time.
// When my work is done, wait for the stream to finish its work.
myStream.EndRead(...);
EndRead should always be called to report some errors have occured. If an error has occured, an EndRead will throw an exception.
No, because the EndRead method is being called (and blocking) within the delegate which is being called asynchronously. So yes, the method with EndRead is blocking, but not on the thread of execution that called BeginRead.

How to create C# Compact Framework non-blocking sockets

I am trying to create a non-blocking socket in WM6 C# but I keep getting the following compiler error:
"Synchronous calls are not supported on non-blocking sockets. Set Blocking=true or use the asynchronous methods."
However when I use the BeginReceive() asynchronous methods, the EndReceive() is blocked. Same for BeginSend() and EndSend(). How do you do a non-blocking socket within the compact framework?
Here's my code. I am not using an AsyncCallback method because I want to return the bytesRecv and bytesSent variables.
private void asyncReceive(byte[] recvBytes, int offset, int size, SocketFlags sf)
{
IAsyncResult asyncResult = null;
int recvBytes = 0;
try
{
asyncResult = _socket.BeginSend(sendBytes, offset, size, sf, null, null);
recvBytes = _socket.EndSend(asyncResult); // <-- This blocks even with _socket.Blocking = false;
}
catch (SocketException)
{
//Check SocketException.ErrorCode...
}
return recvBytes;
}
Looks like you're missing the point- the behavior you're seeing is by design. I'd suggest reading up on async programming in .NET (lots of resources- maybe start here). Non-blocking socket reads/writes with BeginXXX will allow you to start the send/receive, then go do something else and be notified by the kernel when it's done (via the AsyncCallback), at which point you'd call the corresponding EndXXX method to get the result. If you call EndXXX right after the BeginXXX call before the operation is complete, it's designed to block until completion.
To get non-blocking behavior, you'll need to break up your code a bit to handle the callback correctly (eg, marshal the result back to your UI, whatever). You won't have the answer as to how many bytes were sent/received until that's actually been done by the underlying kernel bits.

Difference between NetworkStream.Read() and NetworkStream.BeginRead()?

I need to read from NetworkStream which would send data randomly and the size of data packets also keep varying. I am implementing a multi-threaded application where each thread would have its own stream to read from. If there is no data on the stream, the application should keep waiting for the data to arrive. However, if the server is done sending data and has terminated the session, then it should exit out.
Initially I had utilised the Read method to obtain the data from the stream, but it used to block the thread and kept waiting until data appeared on the stream.
The documentation on MSDN suggests,
If no data is available for reading,
the Read method returns 0. If the
remote host shuts down the connection,
and all available data has been
received, the Read method completes
immediately and return zero bytes.
But in my case, I have never got the Read method to return 0 and exit gracefully. It just waits indefinitely.
In my further investigation, I came across BeginRead which watches the stream and invokes a callback method asynchronously, as soon as it receives the data. I have tried to look for various implementations using this approach as well, however, I was unable to identify when would using BeginRead be beneficial as opposed to Read.
As I look at it, BeginRead has just the advantage of having the async call, which would not block the current thread. But in my application, I already have a separate thread to read and process the data from stream, so that wouldn't make much difference for me.
Can anyone please help me understand the Wait and Exit mechanism for
BeginRead and how is it different from Read?
What would be the best way to implement the desired functionality?
I use BeginRead, but continue blocking the thread using a WaitHandle:
byte[] readBuffer = new byte[32];
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length,
null, null);
WaitHandle handle = asyncReader.AsyncWaitHandle;
// Give the reader 2seconds to respond with a value
bool completed = handle.WaitOne(2000, false);
if (completed)
{
int bytesRead = stream.EndRead(asyncReader);
StringBuilder message = new StringBuilder();
message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));
}
Basically it allows a timeout of the async reads using the WaitHandle and gives you a boolean value (completed) if the read was completed in the set time (2000 in this case).
Here's my full stream reading code copied and pasted from one of my Windows Mobile projects:
private static bool GetResponse(NetworkStream stream, out string response)
{
byte[] readBuffer = new byte[32];
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, null, null);
WaitHandle handle = asyncReader.AsyncWaitHandle;
// Give the reader 2seconds to respond with a value
bool completed = handle.WaitOne(2000, false);
if (completed)
{
int bytesRead = stream.EndRead(asyncReader);
StringBuilder message = new StringBuilder();
message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));
if (bytesRead == readBuffer.Length)
{
// There's possibly more than 32 bytes to read, so get the next
// section of the response
string continuedResponse;
if (GetResponse(stream, out continuedResponse))
{
message.Append(continuedResponse);
}
}
response = message.ToString();
return true;
}
else
{
int bytesRead = stream.EndRead(asyncReader);
if (bytesRead == 0)
{
// 0 bytes were returned, so the read has finished
response = string.Empty;
return true;
}
else
{
throw new TimeoutException(
"The device failed to read in an appropriate amount of time.");
}
}
}
Async I/O can be used to achieve the same amount of I/O in less threads.
As you note, right now your app has one thread per Stream. This is OK with small numbers of connections, but what if you need to support 10000 at once? With async I/O, this is no longer necessary because the read completion callback allows context to be passed identifying the relevant stream. Your reads no longer block, so you don't need one thread per Stream.
Whether you use sync or async I/O, there is a way to detect and handle stream closedown on the relevant API return codes. BeginRead should fail with IOException if the socket has already been closed. A closedown while your async read is pending will trigger a callback, and EndRead will then tell you the state of play.
When your application calls BeginRead,
the system will wait until data is
received or an error occurs, and then
the system will use a separate thread
to execute the specified callback
method, and blocks on EndRead until
the provided NetworkStream reads data
or throws an exception.
Did you try server.ReceiveTimeout? You can set the time which Read() functon will wait for incomming data before returning zero. In your case, this property is probably set to infinite somewhere.
BeginRead is an async process which means your main thread will start execute Read in another process. So now we have 2 parallel processes. if u want to get the result, u have to call EndRead, which will gives the result.
some psudo
BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();
but if your main thread doesn't have anything else to do and u have to need the result, u should call Read.

Categories

Resources