Base Streams


Base streams are stream classes that perform I/O directly with operating system resources such as files, memory, and network resources. In the .NET Framework version 1, only three base streams are available: FileStream , MemoryStream , and NetworkStream .

Note  

In the .NET Framework version 2, there are plans to include a new serial base stream that will enable you to communicate with serial devices such as COM ports or even USB serial devices.

File Stream

One of the most practical uses for streams is reading and writing data to files. The .NET Framework provides the FileStream class that is specifically designed to work with file I/O. Working with files involves creating a file or opening an existing file, reading and writing data to the file, and eventually closing the file from read and write operations.

Note  

Later in this chapter, we ll discuss how stream readers and writers can also allow you to read or write data to a file without having to create a file stream. Readers and writers have constructors that take a path identifying a file and internally create a file stream. Unless you need more granular control of file I/O, it s often a good idea to use readers/writers for accessing files without having to explicitly create file streams.

Creating or Opening a File

Before you can begin reading or writing data to a file, you must create a new file or open an existing file. To do so, you must supply file location information to the FileStream constructor method. Another static class in the System.IO namespace named File is also capable of creating a file stream and performing other file management tasks . For simplicity, we only show how to create a new file using the FileStream constructor. The following code fragment shows how to create a new file named Jim.dat in your current working directory and set the access control for the file to read and write:

C#
 FileStreamMyFileStream=null; try { MyFileStream=newFileStream(".\Jim.dat",FileMode.Create, FileAccess.ReadWrite); } catch(Exceptione) { Console.WriteLine("Failedtocreatefilewiththefollowingerror: " +e.Message); } finally { MyFileStream.Close(); } 
Visual Basic .NET
 DimMyFileStreamAsFileStream Try Openthefile MyFileStream=NewFileStream(.\Jim.dat",FileMode.Create,FileAccess.ReadWrite) CatcheAsException Console.WriteLine(Failedtocreate/openfilestreamwitherror:  _ +e.Message) Finally MyFileStream.Close() EndTry 

It s important to note that many other prototypes for the FileStream constructor enable more control for gaining access to a file. Two main parameters featured in each prototype control file creation: FileMode and FileAccess . FileMode is an enumeration that determines how a file is created. For our sample code, we use the Create enumeration flag that causes the system to create a new file if one does not already exist; otherwise , the flag overwrites an existing file. The other enumeration, FileAccess , determines if your application can read or write to the file. For our sample code, we ve decided to do both.

Reading or Writing to a File

Files are normally constructed as one of two types: text or binary. Text files typically contain printable ASCII and Unicode characters that can normally be viewed using a text reader such as Notepad in Windows. Some examples of text files can be Readme.txt files or even XML files. Binary files, on the other hand, are files that usually contain an ordered set of bytes that are typically not printable. The order (or sequence) of the bytes in a binary file is important to the application that uses the file. An example of a binary file is an MP3 audio file, where bytes are arranged in a sequence to represent encoded audio information. The sequence (or ordering) of bytes in an MP3 file must be ordered in a well- defined way so that an application such as Windows Media Player can play back the audio information correctly. If you try to view a binary file using a text viewer such as Notepad, you ll most likely see a bunch of garbled characters because many of the bytes will typically not be printable ASCII or Unicode characters.

The basic FileStream class allows you to read and write data as an array of bytes. As mentioned earlier, we ll present stream reader and writer classes that allow you to read and write formatted text data or other binary data types to a stream. Using the FileStream object MyFileStream that we created earlier in the chapter, we can begin writing byte-type data to a newly created file. The following code fragment shows how you can write 10 bytes to a file.

C#
 byte[]MyByteArray=newbyte[10]; for(inti=0;i<MyByteArray.Length;i++) { MyByteArray[i]=1; } try { MyFileStream.Write(MyByteArray,0,MyByteArray.Length); } catch(Exceptione) { Console.WriteLine(Writefailedwitherror:  +e.Message); } 
Visual Basic .NET
 DimMyByteArray(10)AsByte DimiAsShort Fori=MyByteArray.GetLowerBound(0)_ ToMyByteArray.GetUpperBound(0)1 MyByteArray(i)=1 Next Try MyFileStream.Write(MyByteArray,0,_ MyByteArray.GetUpperBound(0)) CatcheAsException Console.WriteLine(Writefailedwitherror:  +e.Message) EndTry 

Once bytes are written to a file, you can continue writing additional bytes or start reading the bytes that are already written. As you write bytes to a file, FileStream maintains a stream Position property that knows where the last byte was written to the stream or read from the stream. The Position property can be manipulated directly by setting it to a value offset position in the file, or you can call the Seek method to change the position. The following code fragment demonstrates how to set the position to the beginning of the file using the Seek method on MyFileStream :

C#
 //CodefragmentusingC# try { MyFileStream.Seek(0,SeekOrigin.Begin); } catch(Exceptione) { Console.WriteLine("Seekfailedwitherror: " +e.Message); } 
Visual Basic .NET
 Try MyFileStream.Seek(0,SeekOrigin.Begin) CatcheAsException Console.WriteLine(Seekfailedwitherror:  +e.Message) EndTry 

Once the position is set and there are bytes that follow the position, you can begin reading bytes from the file. Reading bytes is simple because all you have to do is call the FileStream s Read method using a byte array to receive a sequence of bytes from where the stream Position is set. When Read completes successfully, it will return the number of bytes read. It s important to note that Read might return less bytes than you requested and the buffer you supply might not be filled. This normally happens when you reach the end of a file stream. When you reach the end of the file stream and try to read more bytes, Read will return zero. The following code fragment describes how to read a file stream one byte at a time until you reach the end of the file:

C#
 byte[]MyReadBuffer=newbyte[1]; while(true) { intBytesRead; try { BytesRead=MyFileStream.Read(MyReadBuffer,0,MyReadBuffer.Length); } catch(Exceptione) { Console.WriteLine("Readfailedwitherror: " + e.Message); break; } if(BytesRead==0) { Console.WriteLine("Nomorebytestoreadreachedendofstream"); break; } Console.WriteLine("Readbyte-> " +_ MyReadBuffer[0].ToString()); } 
Visual Basic .NET
 DimMyReadBuffer(1)AsByte WhileTrue DimBytesReadAsInteger Try BytesRead=MyFileStream.Read(MyReadBuffer,0,MyReadBuffer.GetUpperBound(0)) CatcheAsException Console.WriteLine(Readfailedwitherror:  +_ e.Message) ExitWhile EndTry IfBytesRead=0Then Console.WriteLine(Nomorebytestoread) ExitWhile EndIf Console.WriteLine(Readbyte->  +_ MyReadBuffer(0).ToString()) EndWhile 
Note  

When reading and writing data to a FileStream , I/O is buffered, which means that data is read and written to a file in the most efficient manner possible. For example, if your application is designed to write data in small chunks using many Write calls, the data written will be collected to memory flushed to the actual disk mechanism at some point. Later in this chapter, we ll talk about a composable stream that provides buffering capabilities to make stream I/O more efficient. However, because the FileStream class provides buffering already, there s no point in using another buffering mechanism above this class.

Closing a File

When all reading and writing to a file is complete, you should close the FileStream to release operating system resources related to the file being manipulated. To close the FileStream , simply call the Close method. If you try to read or write to the file after calling Close , all methods will fail. We demonstrated how to call Close in the try-catch-finally programming block earlier in this chapter when we showed how to create a file stream.

The companion material includes a sample named FileStreamSample that demonstrates how to read and write to a file using the same principles learned so far in the code fragments in this section.

Memory Stream

Memory streams, which perform I/O on memory resources, are another type of base stream. Memory streams are very similar to file streams, except that data is read and written to a memory buffer rather than to a file on a disk. You might even be wondering why there s a memory stream class in the first place, especially when you can allocate memory from an application and write directly to a data buffer. From the standpoint of reading and writing data to memory, a memory stream isn t much different from a buffer. One benefit in having memory accessible in the form of a stream is that streams are composable, meaning that you can build one on top of the other very easily, as mentioned earlier. For example, you can have a memory stream that s layered below a composable encryption stream that s layered below a composable compression stream, so at the end of the day, your application is writing compressed encrypted data to memory using one consistent I/O pattern.

Creating a Memory Stream

The MemoryStream class allows you to create a memory stream with a memory buffer as a backing store of a fixed size or of a dynamic size . The MemoryStream constructor features several prototypes that allow you to pass in a fixed byte-type array buffer that the stream internally uses as a backing store to read and write data. Otherwise, you can specify a size in bytes where the stream will dynamically allocate and manage a buffer for you. The following code fragment creates a memory stream that does not specify a memory size. In this case, the stream will automatically allocate memory as needed as data bytes are written to the stream. Initially, the backing store is set to zero bytes.

C#
 //CodefragmentusingC# MemoryStreamMyMemoryStream=null; try { MyMemoryStream=newMemoryStream(); } catch(Exceptione) { Console.WriteLine(Failedtocreateamemorystreamwitherror:  +e.Message); return; } finally { MyMemoryStream.Close(); } 
Visual Basic .NET
 DimMyMemoryStreamAsSystem.IO.MemoryStream Try MyMemoryStream=NewSystem.IO.MemoryStream() CatcheAsException Console.WriteLine(Failedtocreateamemory  &_  streamwitherror:  +e.Message) Finally MyMemoryStream.Close() EndTry 

Handling Memory Stream I/O

Reading and writing to a MemoryStream is just like handling I/O in a FileStream , as described earlier, except that if you create a memory stream with a fixed buffer, you might experience an IOException if you attempt to write more bytes than the buffer can handle. Another important item worth mentioning is that it s important to call Close on a dynamically allocated memory stream. Close effectively releases the memory associated with the stream, and the data written there is no longer available. The companion material includes a sample named MemoryStreamSample that demonstrates how to read and write data bytes to a dynamically allocated and managed memory stream. As the sample runs, it will monitor how many bytes are actually allocated to the dynamic backing store as bytes are read and written using a property unique to the MemoryStream class named Capacity .

Network Stream

Network streams allow you to communicate between processes over a network or even on the same computer. Network streams rely on the Sockets class from the System.Net namespace as a backing store to communicate from one socket to another, and any application can create one or more sockets to communicate. Network streams require stream-oriented sockets to form a backing store and work only with connection-oriented network protocols such as TCP/IP. Network streams do not work with datagram-based network protocols such as User Datagram Protocol (UDP). Stream-oriented sockets form a virtual connection between two socket pairs to transmit and receive data in sequential order. One of the most popular stream-oriented sockets is one that communicates over the TCP/IP protocol. There are many other ways that sockets can be created and controlled, which is the topic of discussion in Chapter 12. Utilizing network streams requires a complete understanding of how to set up a stream- oriented connected socket pair. For our discussion of network streams, we ll present only TCP/IP sockets. In this chapter, we ll gloss over the details of creating and setting up a stream-oriented socket connection, and the code fragment in this section will assume that a valid socket connection exists.

Compared to file and memory streams, sockets behave quite differently as a backing store. Stream-oriented connected sockets are bidirectional, which means that there are two communication paths within the connected socket pairs. If you write data to Socket A, you will be able to read the same data only on a peer ”Socket B. Figure 2-3 shows bidirectional flow by writing the letters of the alphabet in order from Socket A and receiving the letters on Socket B. In the other direction, we write the numbers 1 through 3 from Socket B and they are read from Socket A. You will not be able to read the data you originally wrote to Socket A from the same socket. The same behavior is true if you write data to Socket B where you ll be required to read the data on Socket A.

click to expand
Figure 2-3: Bidirectional stream-oriented connected sockets

Another major difference in a network stream as compared to other streams is that a network stream doesn t maintain the stream Position property, which means that you can t call Seek or change the Position of the stream. When a network stream reads data from a Socket , the data becomes consumed and is no longer available to be read again. Therefore, the network stream is unable to maintain the Position pointer. For example, in Figure 2-3 on step 2, Socket B can read the A B C characters only one time. Once the characters are read, the characters get removed from the stream. Therefore, if you try to change the Position property or call Seek , you ll get a NotSupportedException .

Creating a Network Stream

Creating a network stream requires having a socket connection established with another socket before you create the network stream to perform I/O. If your socket is not connected, the NetworkStream constructor will raise an ArgumentException . Also, your socket must be running in blocking mode, which is the default behavior when you create a socket. If your socket is non-blocking , an IOException will be raised. For more information about blocking and non- blocking sockets, see Chapter 8. The following code fragment demonstrates how to create a network stream on top of a socket named MySocket :

C#
 SocketMySocket; //Assumewehaveaconnectedsocketalreadycreated. NetworkStreamMyNetworkStream; try { //SetupanetworkstreamonaconnectedSocket MyNetworkStream=newNetworkStream(MySocket,true); } catch(Exceptione) { Console.WriteLine("Failedtocreatenetworkstream " +  "witherror: " +e.Message); } finally { MyNetworkStream.Close(); } 
Visual Basic .NET
 DimMySocketAsSocket 


Network Programming for the Microsoft. NET Framework
Network Programming for the MicrosoftВ® .NET Framework (Pro-Developer)
ISBN: 073561959X
EAN: 2147483647
Year: 2003
Pages: 121

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