WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

Using Network Streams

by Wei-Meng Lee
07/07/2003

In my earlier article on .NET streams (" .NET Streams Explained"), I talked about the various implementation of the Stream class, such as the BufferedStream, FileStream, MemoryStream, and the CryptoStream classes. One class that I did not discuss much is the NetworkStream class.

In this article, I will discuss the use of the NetworkStream class for network communication, and how easy it is to use it for socket programming.

Sending Text Using the NetworkStream Class

The first example that I will illustrate is building a simple client and server to show the exchange of text between two machines. For simplicity, I am writing a synchronous server and the server will simply send back whatever the client has sent it, much like the echo service defined in RFC 862. Also, I will use a VB.NET console application to further simplify the code example.

Building the Server

For all of the examples in this article, I need to import the following namespaces:


Imports System.Net.Sockets
Imports System.Net
Imports System.Text
Imports System.IO
   

Let's first build the server (in all my future articles I will show the complete code before I dissect them; this would be easier for readers to quickly plug-and-play the code into their projects):


Const portNo As Integer = 500
Dim localAdd As System.Net.IPAddress = _
    IPAddress.Parse("127.0.0.1")
Dim listener As New TcpListener(localAdd, portNo)
listener.Start()
Console.WriteLine("Listening...")

Dim tcpClient As TcpClient = listener.AcceptTcpClient()
Dim NWStream As NetworkStream = tcpClient.GetStream
Dim bytesToRead(tcpClient.ReceiveBufferSize) As Byte

'---read incoming stream
Dim numBytesRead As Integer = NWStream.Read(bytesToRead, 0, _
    CInt(tcpClient.ReceiveBufferSize))
Console.WriteLine("Received :" & _
    Encoding.ASCII.GetString(bytesToRead, 0, numBytesRead))

'---write back the text
Console.WriteLine("Sending back : " & _
    Encoding.ASCII.GetString(bytesToRead, 0, numBytesRead))
NWStream.Write(bytesToRead, 0, numBytesRead)

tcpClient.Close()
listener.Stop()
Console.ReadLine()
   

The first few lines are straightforward; set up a TcpListener object and listen at the local port at port 500. Note that the TcpListener constructor in .NET 1.1 now requires the local address to be passed in as a parameter (the old constructor that simply takes in a port number is now obsolete). You can use the IPAddress.Parse() method (from the System.Net namespace) to convert an IP address in string format to the format required by the IPAddress class:


Const portNo As Integer = 500
Dim localAdd As System.Net.IPAddress = _
    IPAddress.Parse("127.0.0.1")
Dim listener As New TcpListener(localAdd, portNo)
listener.Start()
Console.WriteLine("Listening...")
Dim tcpClient As TcpClient = listener.AcceptTcpClient()
   

Once a connection is established, create an instance of the NetworkStream class and use the GetStream() method of the TcpClient class to return a NetworkStream object. Also declare a byte array to store the text sent by the client:


Dim NWStream As NetworkStream = tcpClient.GetStream
Dim bytesToRead(tcpClient.ReceiveBufferSize) As Byte
   

You can read the incoming data sent by the client using the Read() method of the NetworkStream class (all incoming data are stored in a byte array). This method returns the number of total bytes read. Once the data is received, I used the Encoding.ASCII.GetString() method from the System.Text namespace to convert the byte array to string and display it on the screen:


'---read incoming stream
Dim numBytesRead As Integer = NWStream.Read(bytesToRead, 0, _
    CInt(tcpClient.ReceiveBufferSize))
Console.WriteLine("Received :" & _
    Encoding.ASCII.GetString(bytesToRead, 0, numBytesRead))
   

Once the data is read, I will sent it back to the client using the Write() method of the NetworkStream class:


'---write back the text
Console.WriteLine("Sending back : " & _
    Encoding.ASCII.GetString(bytesToRead, 0, numBytesRead))
NWStream.Write(bytesToRead, 0, numBytesRead)
   

Finally, close the TcpClient object and stop the TcpListener object:


        tcpClient.Close()
        listener.Stop()
        Console.ReadLine()
   

Building the client

With the server built, let's now build the client. Here is the code for the client:


Const portNo = 500
Const textToSend = "1234567890098765432111"
Dim tcpclient As New System.Net.Sockets.TcpClient
tcpclient.Connect("127.0.0.1", portNo)

Dim NWStream As NetworkStream = tcpclient.GetStream
Dim bytesToSend As Byte() = Encoding.ASCII.GetBytes(textToSend)
'---send the text
Console.WriteLine("Sending : " & textToSend)
NWStream.Write(bytesToSend, 0, bytesToSend.Length)

'---read back the text
Dim bytesToRead(tcpclient.ReceiveBufferSize) As Byte
Dim numBytesRead = NWStream.Read(bytesToRead, 0, _
    tcpclient.ReceiveBufferSize)
Console.WriteLine("Received : " & _
    Encoding.ASCII.GetString(bytesToRead, 0, _
                                numBytesRead))
Console.ReadLine()
tcpclient.Close()
   

If you compare the code for the client with that of the server, you will notice that they are very similar. So I won't go into details here.

To test the sample applications, run the server application followed by the client. You should see something like this:

Sending and receiving of data by the client and the server
Figure 1. Sending and receiving of data by the client and the server

Receiving Large Incoming Data

In the previous example, I used the constant ReceiveBufferSize (which is 8192 bytes) as the maximum number of bytes to read as incoming text:


Dim bytesToRead(tcpClient.ReceiveBufferSize) As Byte
'---read incoming stream
Dim numBytesRead As Integer = NWStream.Read(bytesToRead, 0, _
    CInt(tcpClient.ReceiveBufferSize))
   

What happen if the incoming data is more than the size you have defined? In the next example, I will simulate reading a long string of text by reading 10 bytes at a time. Here is my modified server:

        
Const BUFFER_SIZE As Integer = 10

Dim bytesToRead(BUFFER_SIZE) As Byte
'---read incoming stream
Dim textReceived As String
Do
    Dim numBytesRead As Integer = _
        NWStream.Read(bytesToRead, 0, BUFFER_SIZE)
    textReceived += Encoding.ASCII.GetString(bytesToRead, _
                    0, numBytesRead)
    Console.WriteLine("Received :" & _
                        Encoding.ASCII.GetString(bytesToRead, _
                        0, numBytesRead))
Loop Until Not NWStream.DataAvailable

'---write back the text
Console.WriteLine("Sending back : " & textReceived)
NWStream.Write(Encoding.ASCII.GetBytes(textReceived), 0, _
                textReceived.Length)
   

Instead of reading all of the incoming data at once, I will read 10 bytes at a time by using a loop. The DataAvailable property from the NetworkStream class will indicate whether additional data is available on the stream object. If it is, I will continue the read process until no more data is available.

If you run the server and the client, you should see the following:

Receiving data in "blocks"
Figure 2. Receiving data in "blocks"

Sending and Receiving Binary Data

The last example that I want to illustrate is to show how to send binary data using the NetworkStream class.

Here is the complete source:


Const portNo As Integer = 500
Dim localAdd As System.Net.IPAddress = _
    IPAddress.Parse("127.0.0.1")
Dim listener As New TcpListener(localAdd, portNo)
listener.Start()
Console.WriteLine("Listening...")

Dim tcpClient As TcpClient = listener.AcceptTcpClient()
Dim NWStream As NetworkStream = tcpClient.GetStream
Dim bytesToRead(tcpClient.ReceiveBufferSize) As Byte

'---read incoming stream
Dim numBytesRead As Integer = NWStream.Read(bytesToRead, _
                        0, CInt(tcpClient.ReceiveBufferSize))

'---write the bytes to file
Const FILE_NAME = "c:\image.gif"
Dim fs As System.IO.FileStream
fs = New FileStream(FILE_NAME, FileMode.CreateNew, _
                    FileAccess.Write)
fs.Write(bytesToRead, 0, numBytesRead)
fs.Close()

tcpClient.Close()
listener.Stop()
Console.ReadLine()
   

Similar to the last example, once I have read the incoming data into the byte array:


'---read incoming stream
Dim numBytesRead As Integer = NWStream.Read(bytesToRead, _
                        0, CInt(tcpClient.ReceiveBufferSize))
   

I save the read data into a file using the FileStream object (which I have covered in the ".NET Streams Explained" article):


'---write the bytes to file
Const FILE_NAME = "c:\image.gif"
Dim fs As System.IO.FileStream
fs = New FileStream(FILE_NAME, FileMode.CreateNew, _
                    FileAccess.Write)
fs.Write(bytesToRead, 0, numBytesRead)
fs.Close()
   

Here is the code for the client:


Const portNo = 500
Const FILE_NAME = "c:\ondotnet_logo.gif"
Dim tcpClient As New System.Net.Sockets.TcpClient
tcpClient.Connect("127.0.0.1", portNo)

Dim NWStream As NetworkStream = tcpclient.GetStream
Dim bytesToSend(tcpClient.ReceiveBufferSize) As Byte

Dim fs As FileStream
fs = New FileStream(FILE_NAME, FileMode.Open, _
                    FileAccess.Read)
Dim numBytesRead As Integer = fs.Read(bytesToSend, _
                    0, bytesToSend.Length)
fs.Close()

'---send the text
Console.WriteLine("Sending ...")
NWStream.Write(bytesToSend, 0, numBytesRead)

Console.ReadLine()
tcpclient.Close()
   

As on the server, I used the FileStream class to open a file and read the data to be sent.

Things to Note About the NetworkStream Class

Unlike most other Stream implementations, the use of the NetworkStream class is more restrictive. For example, the CanSeek() properties of the NetworkStream class are not supported and always return false. Similarly, the Length() and Position() properties will throw a NotSupportedException.

Likewise, it is not possible to perform a Seek() operation, and the SetLength() method will also throw a NotSupportedException.

On the other hand, the NetworkStream class has made network programming very easy and encapsulates much of the complexity of socket programming. Developers who are familiar with streams programming can use the NetworkStream class with ease.

Wei-Meng Lee (Microsoft MVP) http://weimenglee.blogspot.com is a technologist and founder of Developer Learning Solutions http://www.developerlearningsolutions.com, a technology company specializing in hands-on training on the latest Microsoft technologies.


Return to ONDotnet.com