11.12 Use TCP Asynchronously


Problem

You need to write data to the network stream one " chunk " at a time, without blocking the rest of your code. This technique might be used if you wanted to stream a large file over the network.

Solution

Create a separate class that will handle the asynchronous streaming of the data. You can start streaming a block of data using the NetworkStream.BeginWrite method and supply a callback method. When the callback is triggered, send the next block.

Discussion

The NetworkStream includes basic support for asynchronous use through the BeginRead and BeginWrite methods. Using these methods , you can send or receive a block of data on one of the threads provided by the .NET runtime's thread pool, without blocking your code. This recipe demonstrates the technique with asynchronous writing.

When sending data asynchronously, you must send raw binary data (an array of bytes). It's up to you to choose the amount that you want to send or receive at a time. The following example rewrites the threaded server from recipe 11.11 so that each ClientHandler class sends a large amount of data read from a file. This data is sent asynchronously, which means that ClientHandler could continue to perform other tasks . (In this example, it simply polls the network stream for messages sent from the client.)

One advantage of the approach used in this recipe is that the entire content from the file is never held in memory at once. Instead, it is retrieved just before a new block is sent. Another advantage is that the server can abort the operation at any time. In this example, the client reads only a third of the data before disconnecting. The server then sets a Boolean member variable named fileStop to indicate to the callback that no more data should be sent.

Here's the revised ClientHandler class. Notice that NetworkStream and FileStream are now tracked as member variables , so the callback method can access them. The TcpServerTest class doesn't need any changes.

 using System; using System.Net; using System.Net.Sockets; using System.IO; using SharedComponent; public class ClientHandler {     private TcpClient client;     private string ID;     // The amount that will be written in one block (2 KB).     private int bufferSize = 2048;     // The buffer that holds the data to write.     private byte[] buffer;     // Used to read data from a file.     private FileStream fileStream;     // Used to communicate with the client.     private NetworkStream networkStream;     // A signal to stop sending data.     private bool fileStop = false;     public ClientHandler(TcpClient client, string ID) {         this.buffer = new byte[bufferSize];         this.client = client;         this.ID = ID;     }     public void Start() {         // Retrieve the network stream.         networkStream = client.GetStream();         // Create objects for sending and receiving text.         BinaryWriter w = new BinaryWriter(networkStream);         BinaryReader r = new BinaryReader(networkStream);         if (r.ReadString() == ClientMessages.RequestConnect) {             w.Write(ServerMessages.AcknowledgeOK);             Console.WriteLine(ID + ": Connection completed.");             string message = "";             while (message != ClientMessages.Disconnect) {                 message = r.ReadString();                 if (message == ClientMessages.RequestData) {                     // The filename could be supplied by the client,                     // but in this example a test file is hard-coded.                     fileStream = new FileStream("test.bin", FileMode.Open);                     // Send the file size (this is how the client knows                     // how much to read).                     w.Write(fileStream.Length.ToString());                     // This method will start an asynchronous operation.                     StreamData(null);                 }             }             fileStop = true;             Console.WriteLine(ID + ": Disconnect request received.");         } else {             Console.WriteLine(ID + ": Could not complete connection.");         }         // Clean up.         client.Close();         Console.WriteLine(ID + ": Client connection closed.");         Console.ReadLine();     }     private void StreamData(IAsyncResult asyncResult) {         // Abort if the client has disconnected.          if (fileStop == true) {             fileStop = false;             return;         }         if (asyncResult != null) {             // One block has been written asynchronously.             networkStream.EndWrite(asyncResult);         }         // Get the next block from the file.         int bytesRead = fileStream.Read(buffer, 0, buffer.Length);         // If no bytes are read, the stream is at the end of the file.         if (bytesRead > 0) {             Console.WriteLine("Streaming new block.");             // Write the next block to the network stream.             networkStream.BeginWrite(buffer, 0, buffer.Length,               new AsyncCallback(StreamData), null);         } else {             // End the operation.             Console.WriteLine("File streaming complete.");             fileStream.Close();         }     } } 

You could use a similar pattern to read the data asynchronously on the client side.




C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net