No Stream.ReadByteAsync? - c#

I'm writing a game server using it's own protocol on top of TCP.
The packets are of the form varint packet_length followed by that amount of bytes.
To read a varint, I must read byte by byte. But Stream or NetworkStream does not provide a ReadByteAsync method, which means the task and therefore the thread will block if there is no data available from the socket yet. Of course I would wrap NetworkSteam with a BufferedStream but even BufferedStream's ReadByte uses synchronous Read to read to it's buffer. This will block.
This is how the server roughly looks like:
public static void Main(string[] args)
{
var listener = new TcpListener(new IPEndPoint(IPAddress.Any, 25000));
while (true)
{
var client = listener.AcceptTcpClientAsync().Result;
HandleClient(client).ContinueWith(t => {
if (t.IsFaulted)
{
Console.WriteLine(t.Exception);
}
});
}
}
public static async Task HandleClient(TcpClient client)
{
await Task.Yield();
while (true)
{
// read packet
// using ReadByte here would block if data is not available, which means I'm wasting a thread?
}
}
Why is ReadByteAsync not there? Or am I missing something?

Just use the already existing method ReadAsync, bending it in your favor:
private static async Task<byte> ReadByteAsync(Stream s)
{
byte[] buffer = new byte[1];
await s.ReadAsync(buffer, 0, 1);
return buffer[0];
}
Homework for ya: factor out the buffer into a class-level field so it doesn't get created each method call.

Related

.NET Socket ReadAsync blocked during write loop Async / Await

I am writing a TCP server using Async / Await that needs to send a list of messages to connected clients, based on what it receives from each client. Between each message sent to the client, I need to:
wait for an acknowledgement/response then send the next messages
resend the command if no acknowledgement after 5 seconds
To do this, I am setting a ResponseReceived property on my ConnClient class when the expected response comes in. Then, in the ConnClient.SendListAsync routine, I am checking to see if the property has been changed after sending each command. However, incoming responses are not read until the SendListAsync sends all messages, as can be seen in my debug statements below:
Sending Initial Message.
Received response, generate list of 3 initial commands and send them.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
SendListAsync 5 second timeout w/o response.
Received response.
Received response.
Received response.
Question: How can I properly prevent ConnClient.SendListAsync from blocking incoming reads?
public class Svr
{
TcpListener listener;
public async Task Listen(IPAddress iP, int port)
{
listener = new TcpListener(iP, port);
listener.Start();
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
ConnClient cc = new ConnClient(client);
await Receive(ConnClient);
}
}
async Task Receive(ConnClient cc)
{
var headerSize = sizeof(short);
byte[] buffer = new byte[4000];
//Send initial msg
await cc.socket.GetStream().WriteAsync(Strings.InitialMsg, 0, Strings.InitialMsg.Length);
while (true)
{
buffer = new byte[headerSize];
if (!await ReadToBuffer(cc.socket.GetStream(), buffer, headerSize))
return;
var length = BitConverter.ToUInt16(new byte[2] { buffer[1], buffer[0] }, 0 );
buffer = new byte[length];
if (!await ReadToBuffer(cc.socket.GetStream(), buffer, length))
return;
await DoSomethingBasedOnReceived(messageBuffer, cc);
}
}
async Task<Boolean> ReadToBuffer(NetworkStream stream, byte[] buffer, int bytesToRead)
{
int offset = 0;
while (offset < bytesToRead)
{
var length = await stream.ReadAsync(buffer, offset, bytesToRead - offset);
if (length == 0)
return false;
offset += length;
}
return true;
}
public async Task DoSomethingBasedOnReceived(byte[] messageBuffer, ConnClient cc)
{
await SomeLogicToSetTheRRFlagIfMessageApplicable(messageBuffer, cc);
List<byte[]> ListOfMessagesToSend = SomeLogicToDetermineListOfMessages(messageBuffer);
await cc.SendListAsync(ListOfMessagesToSend);
}
}
ConnClient Class, representing an individual connected client.
public class ConnClient
{
public TcpClient socket { get; set; }
public Boolean ResponseReceived { get; set; }
public ConnClient (TcpClient cc)
{socket = cc}
public async Task SendListAsync(List<byte[]> messageList)
{
foreach (byte[] msg in messageList)
{
this.ResponseReceived = false;
await stream.WriteAsync(msg, 0, msg.Length);
int waitedSoFar = 0;
while (waitedSoFar < 5000)
{
if (this.ResponseReceived == true)
{
break;
}
waitedSoFar += 100;
await Task.Delay(100);
}
}
}
}
Your first problem is you will not be able to accept new clients.
while (true)
{
// accept the next connection
TcpClient client = await listener.AcceptTcpClientAsync();
// receive and send list
ConnClient cc = new ConnClient(client);
await Receive(ConnClient);
// the loop cannot continue to receive the next connection
// until you have done with your receive
}
You will need to execute Receive independently so you can wait for the next connection, you can either call it without an await (which will run as an async void), or offload it to a new task .
Remove the await
Receive(ConnClient);
Offloading
Task.Run(() => Receive(ConnClient));
Your second problem is your client is held up while sending and will not be able to receive. Once again you will to either offload, or run without the await.
As #PeterDuniho mentions
Given that the OP is already using async/await, and given that
Receive() is already async, there's no reason to use Task.Run(). It's
fire-and-forget either way (unless they change their code to store the
returned task), so they might as well just fire-and-forget the call to
Receive() as wrap it in a call to Task.Run().
Note : Creating a scalable client/server socket solution is not trivial, and i am not attempting to show this. However, it will solve your current problem.
Either way, be very mindful of errors. Since both the proposed solutions will run unobserved, exceptions will need to be handled

Proxying WebSocket messages between two streams

I have a HTTP proxy server which acts as a middle-man. It basically does the following:
Listen for client-browser request
Forward the request to the server
Parse the server response
Forward the response back to client-browser
So basically there is one NetworkStream, or even more often a SslStream between a client-browser and the proxy, and another one between the proxy and a server.
A requirement has arisen to also forward WebSocket traffic between a client and a server.
So now when a client-browser requests a connection upgrade to websocket, and the remote server responds with HTTP code 101, the proxy server maintains these connections in order to forward further messages from client to server and vice versa.
So after the proxy has received a message from the remote server saying it's ready to switch protocols, it needs to enter a loop where both client and server streams are polled for data, and where any received data is forwarded to the other party.
The problem
WebSocket allows both sides to send messages at any time. This is especially a problem with control messages such as ping/pong, where any side could send a ping at any time and the other side is expected to reply with a pong in a timely manner. Now consider having two instances of SslStream which don't have DataAvailable property, where the only way to read data is to call Read/ReadAsync which might not return until some data is available. Consider the following pseudo-code:
public async Task GetMessage()
{
// All these methods that we await read from the source stream
byte[] firstByte = await GetFirstByte(); // 1-byte buffer
byte[] messageLengthBytes = await GetMessageLengthBytes();
uint messageLength = GetMessageLength(messageLengthBytes);
bool isMessageMasked = DetermineIfMessageMasked(messageLengthBytes);
byte[] maskBytes;
if (isMessageMasked)
{
maskBytes = await GetMaskBytes();
}
byte[] messagePayload = await GetMessagePayload(messageLength);
// This method writes to the destination stream
await ComposeAndForwardMessageToOtherParty(firstByte, messageLengthBytes, maskBytes, messagePayload);
}
The above pseudo code reads from one stream and writes to the other. The problem is that the above procedure needs to be run for both streams simultaneously, because we don't know which side would send a message to the other at any given point in time. However, it is impossible to perform a write operation while there is a read operation active. And because we don't have the means necessary to poll for incoming data, read operations have to be blocking. That means if we start read operations for both streams at the same time, we can forget about writing to them. One stream will eventually return some data, but we won't be able to send that data to the other stream as it will still be busy trying to read. And that might take a while, at least until the side that owns that stream sends a ping request.
Thanks to comments from #MarcGravell we've learned that independent read/write operations are supported with network streams, i.e. NetworkStream acts as two independent pipes - one read, one write - it is fully duplex.
Therefore, proxying WebSocket messages can be as easy as just starting two independent tasks, one to read from client stream and write to server stream, and another to read from server stream and write to client stream.
If it can be of any help to anyone searching for it, here is how I implemented that:
public class WebSocketRequestHandler
{
private const int MaxMessageLength = 0x7FFFFFFF;
private const byte LengthBitMask = 0x7F;
private const byte MaskBitMask = 0x80;
private delegate Task WriteStreamAsyncDelegate(byte[] buffer, int offset, int count, CancellationToken cancellationToken);
private delegate Task<byte[]> BufferStreamAsyncDelegate(int count, CancellationToken cancellationToken);
public async Task HandleWebSocketMessagesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var clientListener = ListenForClientMessages(cancellationToken);
var serverListener = ListenForServerMessages(cancellationToken);
await Task.WhenAll(clientListener, serverListener);
}
private async Task ListenForClientMessages(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
await ListenForMessages(YOUR_CLIENT_STREAM_BUFFER_METHOD_DELEGATE, YOUR_SERVER_STREAM_WRITE_METHOD_DELEGATE, cancellationToken);
}
}
private async Task ListenForServerMessages(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
cancellationToken.ThrowIfCancellationRequested();
await ListenForMessages(YOUR_SERVER_STREAM_BUFFER_METHOD_DELEGATE, YOUR_CLIENT_STREAM_WRITE_METHOD_DELEGATE, cancellationToken);
}
}
private static async Task ListenForMessages(BufferStreamAsyncDelegate sourceStreamReader,
WriteStreamAsyncDelegate destinationStreamWriter,
CancellationToken cancellationToken)
{
var messageBuilder = new List<byte>();
var firstByte = await sourceStreamReader(1, cancellationToken);
messageBuilder.AddRange(firstByte);
var lengthBytes = await GetLengthBytes(sourceStreamReader, cancellationToken);
messageBuilder.AddRange(lengthBytes);
var isMaskBitSet = (lengthBytes[0] & MaskBitMask) != 0;
var length = GetMessageLength(lengthBytes);
if (isMaskBitSet)
{
var maskBytes = await sourceStreamReader(4, cancellationToken);
messageBuilder.AddRange(maskBytes);
}
var messagePayloadBytes = await sourceStreamReader(length, cancellationToken);
messageBuilder.AddRange(messagePayloadBytes);
await destinationStreamWriter(messageBuilder.ToArray(), 0, messageBuilder.Count, cancellationToken);
}
private static async Task<byte[]> GetLengthBytes(BufferStreamAsyncDelegate sourceStreamReader, CancellationToken cancellationToken)
{
var lengthBytes = new List<byte>();
var firstLengthByte = await sourceStreamReader(1, cancellationToken);
lengthBytes.AddRange(firstLengthByte);
var lengthByteValue = firstLengthByte[0] & LengthBitMask;
if (lengthByteValue <= 125)
{
return lengthBytes.ToArray();
}
switch (lengthByteValue)
{
case 126:
{
var secondLengthBytes = await sourceStreamReader(2, cancellationToken);
lengthBytes.AddRange(secondLengthBytes);
return lengthBytes.ToArray();
}
case 127:
{
var secondLengthBytes = await sourceStreamReader(8, cancellationToken);
lengthBytes.AddRange(secondLengthBytes);
return lengthBytes.ToArray();
}
default:
throw new Exception($"Unexpected first length byte value: {lengthByteValue}");
}
}
private static int GetMessageLength(byte[] lengthBytes)
{
byte[] subArray;
switch (lengthBytes.Length)
{
case 1:
return lengthBytes[0] & LengthBitMask;
case 3:
if (!BitConverter.IsLittleEndian)
{
return BitConverter.ToUInt16(lengthBytes, 1);
}
subArray = lengthBytes.SubArray(1, 2);
Array.Reverse(subArray);
return BitConverter.ToUInt16(subArray, 0);
case 9:
subArray = lengthBytes.SubArray(1, 8);
Array.Reverse(subArray);
var retVal = BitConverter.ToUInt64(subArray, 0);
if (retVal > MaxMessageLength)
{
throw new Exception($"Unexpected payload length: {retVal}");
}
return (int) retVal;
default:
throw new Exception($"Impossibru!!1 The length of lengthBytes array was: '{lengthBytes.Length}'");
}
}
}
It can be used by just calling await handler.HandleWebSocketMessagesAsync(cancellationToken) after the initial handshake has been performed.
The SubArray method is taken from here: https://stackoverflow.com/a/943650/828023 (also from #Marc haha)

Server communication via async/await?

I want to create Socket message sending via TAP via async/await.
After reading this answer and this one - I decided to create a fully working sample :
So what have I tried :
I took the TAP extenstion methods from here (all ok) : and I test it in console cmd :
Reciever Code
public static class SocketExtensions
{
public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
{
return Task.Factory.FromAsync<int>(
socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndReceive);
}
public static async Task<byte[]> ReceiveExactTaskAsync(this Socket socket, int len)
{
byte[] buf = new byte[len];
int totalRead = 0;
do{
int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead);
if (read <= 0) throw new SocketException();
totalRead += read;
}while (totalRead != buf.Length);
return buf;
}
public static Task ConnectTaskAsync(this Socket socket, string host, int port)
{
return Task.Factory.FromAsync(
socket.BeginConnect(host, port, null, null),
socket.EndConnect);
}
public static Task SendTaskAsync(this Socket socket, byte[] buffer)
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
socket.EndSend);
}
}
static void Main()
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.ConnectTaskAsync("127.0.0.1", 443);
var buf1 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf1.Result));
var buf2 = s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf2.Result));
Console.ReadLine();
}
Sender Code :
// use same extension method class like above ....^
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
s.ConnectTaskAsync( "127.0.0.1" , 443);
s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
s.Close();
Console.ReadLine();
}
notice I removed the async from main since im testing it in console.
Question ,
According to link above , the code should work
However I'm getting no exception and it's just hangs on that line :
Console.Write(Encoding.UTF8.GetString(buf1.Result));
(First I run receiver , then I run sender)
What am I missing?
the problem comes from the "notice I removed the async from main since im testing it in console.".
You need to wait for the operation to complete before doing the next step. The code you used as an example pauses at each await for the operation to complete, your code just goes straight through.
You may be able to fix this by putting a .Wait() after each operation that would have had a await or by running this function inside a threadpool thread via Task.Run(, however I think it is better to know when you should use async and when you should not.
Async should be used when you have other work you could have the thread be doing, very commonly that "other work" will be things like processing UI messages on a WinForms project or accepting new connections on a ASP.NET site. In a console application there is no other work your program could be doing while it waits, so in that situation it would be more appropriate to use the synchronous version of the functions instead.
P.S. You made the comment after I posted "that's why I remove the async awaits and used Task.result", just so you know never ever1 combine code that uses await and code that blocks the synchronization contest (by using things like Task.Result or Task.Wait(), you will likely cause your code to deadlock and stop functioning!
It is not a issue for your current example because console applications do not have a synchronization context, but if you copied and pasted this code to something that did you could easily lock up your program.
1: Ok, you could combine await and blocking code but there are rules you need to follow, but if you know enough to dispute my what I am saying you know enough to safely do it. If you don't know how to safely do it just avoid doing it
since you do not wait for the threads to do their work and then call s.Close() the socket will be closed before any traffic is sent out.
You would have to either remove the s.Close() call or wait until the calls are complete, for instance via
Task connect = s.ConnectTaskAsync( "127.0.0.1" , 443);
connect.Wait(); // you have to connect before trying to send
Task sendData = s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
sendData.Wait(); // wait for the data to be sent before calling s.Close()
s.Close();
or you could box it in a method and Wait for that method to complete. The end result is to not call Close before completing the previous calls.
void Main()
{
Socket s = new Socket(SocketType.Stream , ProtocolType.Tcp);
Task worker = DoWorkAsync(s);
worker.Wait();
s.Close();
Console.ReadLine();
}
private async Task DoWorkAsync(Socket s){
await s.ConnectTaskAsync( "127.0.0.1" , 443);
await s.SendTaskAsync(Encoding.UTF8.GetBytes("hello"));
}

Stopping threads in C#

I need to make TcpClient event driven rather than polling for messages all the time, so I thought: I will create a thread that would wait for a message to come and fire an event once it does. Here is a general idea:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace ThreadsTesting
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
//imitate a remote client connecting
TcpClient remoteClient = new TcpClient();
remoteClient.Connect(IPAddress.Parse("127.0.0.1"), 80);
//start listening to messages
p.startMessageListener();
//send some fake messages from the remote client to our server
for (int i = 0; i < 5; i++)
{
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
}
//sleep for a while to make sure the cpu is not used
Console.WriteLine("Sleeping for 2sec");
Thread.Sleep(2000);
//attempt to stop the server
p.stopMessageListener();
Console.ReadKey();
}
private CancellationTokenSource cSource;
private Task listener;
private TcpListener server;
private TcpClient client;
public Program()
{
server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
server.Start();
}
private void startMessageListener()
{
client = server.AcceptTcpClient();
//start listening to the messages
cSource = new CancellationTokenSource();
listener = Task.Factory.StartNew(() => listenToMessages(cSource.Token), cSource.Token);
}
private void stopMessageListener()
{
Console.Out.WriteLine("Close requested");
//send cancelation signal and wait for the thread to finish
cSource.Cancel();
listener.Wait();
Console.WriteLine("Closed");
}
private void listenToMessages(CancellationToken token)
{
NetworkStream stream = client.GetStream();
//check if cancelation requested
while (!token.IsCancellationRequested)
{
//wait for the data to arrive
while (!stream.DataAvailable)
{ }
//read the data (always 1 byte - the message will always be 1 byte)
byte[] bytes = new byte[1];
stream.Read(bytes, 0, 1);
Console.WriteLine("Got Data");
//fire the event
}
}
}
}
This for obvious reasons doesn't work correctly:
while (!stream.DataAvailable) blocks the thread and uses always 25% CPU (on 4-core CPU), even if no data is there.
listener.Wait(); will wait for ever since the while loop doesn't pick up that cancel has been called.
My alternative solution would be using async calls within the listenToMessages method:
private async Task listenToMessages(CancellationToken token)
{
NetworkStream stream = client.GetStream();
//check if cancelation requested
while (!token.IsCancellationRequested)
{
//read the data
byte[] bytes = new byte[1];
await stream.ReadAsync(bytes, 0, 1, token);
Console.WriteLine("Got Data");
//fire the event
}
}
This works exactly as I expected:
The CPU is not blocked if there are no messages in the queue, but we are still waiting for them
Cancelation request is picked up correctly and thread finished as expected
I wanted to go further though. Since listenToMessages now returns a Task itself, I thought there is no need of starting a task that would execute that method. Here is what I did:
private void startMessageListener()
{
client = server.AcceptTcpClient();
//start listening to the messages
cSource = new CancellationTokenSource();
listener = listenToMessages(cSource.Token);
}
This doesn't work as I have expected in the sence that when Cancel() is called, the ReadAsync() method doesn't seem to pick up the cancelation message from the token, and the thread doesn't stop, instead it is stuck on the ReadAsync() line.
Any idea why is this happening? I would think the ReadAsync will still pick up the token, as it did before...
Thanks for all your time and help.
-- EDIT --
Ok so after more in depth evaluation my solution no.2 doesn't really work as expected:
the thread itself ends to the caller and so the caller can continue. However, the thread is not "dead", so if we send some data it will execute once more!
Here is an example:
//send some fake messages from the remote client to our server
for (int i = 0; i < 5; i++)
{
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
}
Console.WriteLine("Sleeping for 2sec");
Thread.Sleep(2000);
//attempt to stop the server
p.stopListeners();
//check what will happen if we try to write now
remoteClient.GetStream().Write(new byte[] { 0x80 }, 0, 1);
Thread.Sleep(200);
Console.ReadKey();
This will output the message "Got Data" even though in theory we stopped! I will investigate further and report on my findings.
With modern libraries, any time you type new Thread, you've already got legacy code.
The core solution for your situation is asynchronous socket methods. There are a few ways to approach your API design, though: Rx, TPL Dataflow, and plain TAP come to mind. If you truly want events then EAP is an option.
I have a library of EAP sockets here. It does require a synchronizing context, so you'd have to use something like ActionDispatcher (included in the same library) if you need to use it from a Console application (you don't need this if you're using it from WinForms/WPF).
ReadAsync doesn't seem to support cancellation on a NetworkStream - have a look at the answers in this thread:
NetworkStream.ReadAsync with a cancellation token never cancels

C#: How to set AsyncWaitHandle in Compact Framework?

I'm using a TcpClient in one of my Compact Framework 2.0 applications. I want to receive some information from a TCP server.
As the Compact Framework does not support the timeout mechanisms of the "large" framework, I'm trying to implement my own timeout-thing. Basically, I want to do the following:
IAsyncResult result = networkStream.BeginRead(buffer, 0, size, ..., networkStream);
if (!result.AsyncWaitHandle.WaitOne(5000, false))
// Handle timeout
private void ReceiveFinished(IAsyncResult ar)
{
NetworkStream stream = (NetworkStream)ar.AsyncState;
int numBytes = stream.EndRead(ar);
// SIGNAL IASYNCRESULT.ASYNCWAITHANDLE HERE ... HOW??
}
I'd like to call Set for the IAsyncResult.AsyncWaitHandle, but it doesn't have such a method and I don't know which implementation to cast it to.
How do I set the wait handle? Or is it automatically set by calling EndRead? The documentation suggests that I'd have to call Set myself...
Thanks for any help!
UPDATE
Seems that the wait handle is set automatically when calling EndRead - but it's not in the docs. Can somebody confirm this?
UPDATE 2
Wrote client.BeginRead in my sample code. Of course, BeginRead is called on the NetworkStream...
I think you have a misunderstanding about async IO with TCP.
To kick off async IO, call stream.BeginRead().
In the callback, you call EndRead on the stream.
You don't call BeginRead on the TcpClient, as your code shows. Your app doesn't ever signal the WaitHandle. The IO layer will invoke your callback when the waithandle is signalled, in other words when the async Read happens.
In your callback, normally you'd call BeginRead again, on the stream, if it's possible that you'll be receiving more data.
You can see a clear example in this answer.
Before starting the BeginRead/EndRead dance,
you may want to do an async Connect on the TcpClient - then you would use BeginConnect. But that's done just once. Alternatively, you might want a synchronous connect, in which case you just call TcpClient.Connect().
example code:
private class AsyncState
{
public NetworkStream ns;
public ManualResetEvent e;
public byte[] b;
}
public void Run()
{
NetworkStream networkStream = ...;
byte[] buffer = new byte[1024];
var completedEvent = new ManualResetEvent(false);
networkStream.BeginRead(buffer, 0, buffer.Length,
AsyncRead,
new AsyncState
{
b = buffer,
ns = networkStream,
e = completedEvent
});
// do other stuff here. ...
// finally, wait for the reading to complete
completedEvent.WaitOne();
}
private void AsyncRead(IAsyncResult ar)
{
AsyncState state = ar as AsyncState;
int n = state.ns.EndRead(ar);
if (n == 0)
{
// signal completion
state.e.Set();
return;
}
// state.buffer now contains the bytes read
// do something with it here...
// for example, dump it into a filesystem file.
// read again
state.ns.BeginRead(state.b, 0, state.b.Length, AsyncRead, state);
}

Categories

Resources