This section covers a couple of utility classes to make Web programming easier when dealing with URIs and IP addresses.
uri and UriBuilder are two classes in the System (not System.Net) namespace, and they are both intended to represent a URI. UriBuilder allows you to build a URI given the strings for the component parts, and the Uri class allows you to parse, combine, and compare URIs.
For the uri class, the constructor requires a completed URI string:
uri MSPage= new
The class exposes a large number of read-only properties. A Uri object is not intended to be modified once it has been constructed:
URIBuilder, however, implements fewer properties, just enough to allow you to build up a complete URI. These properties are read-write.
You can supply the components to build up a URI to the constructor:
uri MSPage= new .
UriBuilder(‘http’, ‘www.Microsoft.com·, 80, ·SomeFolderISomeFile.htm·);
Or.you can build the components by assigning values to the properties:
Once you have completed. initializing the UriBuilder, you can obtain the corresponding Uri object with the Uri property:
uri CompletedUri = MSPage.Uri;
IP Addresses and DNS Names
On the Internet, you identify servers as well as clients by IP address or host name (also referred to as a DNS name). Generally speaking, the host name is the human-friendly name that you type in a Web browser window, such as www.csharpaid.com. An IP address is the identifier computers use to identify each other. IP addresses are the identifiers used. to ensure that Web requests
and responses reach the appropriate machines. It is even possible for a computer to have more than one IP address.
Today, IP addresses are typically a 32-bit value. An example of a 32-bit IP address is 192.168.1.100.This format of IP address is referred to as Internet Protocol version 4. Because there are now so many computers and other devices vying for a spot on the Internet, a newer type of address was developed – Internet Protocol version 6. IPv6 provides a 64-bit IP address. IPv6 can potentially provide a maximum number of about 3 x 10 Exp 28 unique addresses. You will find that the .NET Framework allows your applications to work with both IPv4 and IPv6.
For host names to work, you must first send a network request to translate the host name into an IP address, a task carried out by one or more DNS servers.
A DNS server stores a table mapping host names to IP addresses for all the computers it knows about, as well as the IP addresses of other DNS servers to look up the host names it does not know about. Your local computer should always know about at least one DNS server. Network administrators configure this information when a computer is set up.Before sending out a request, your computer will first ask the DNS server to tell it the IP address corresponding to the host name you have typed in. Once armed with the correct IP address, the computer call address the request and send it over the network. All of this work normally happens behind the scenes while the user is browsing the Web.
.NET Classes for IP Addresses
The .NET Framework supplies a number of classes that are able to assist with the process of looking up IP addresses and finding information about host computers.
IPAddress represents an IP address. The address itself is available as the GetAddressBytes property and may be converted. to a dotted decimal format with the ToString() method. IPAddress also implements a static Parse() method, which effectively performs the reverse conversion of ToString() – converting from a dotted. decimal string to an IPAddress:
In this example, the byte integer address is assigned. a binary representation of the IP address, and the string ipString is assigned the text’ 188.8.131.52.
IPAddress also provides a number of constant static fields to return special addresses. For example, the Loopback address allows a machine to send messages to itself whereas the Broadcast address allows multicasting to the local network:
The IPHostEntry class encapsulates information relating to a particular host computer. This class makes the host name available via the HostName property (which returns a string), and the AddressList property returns an array of IPAddress objects. You are going to use the IPHostEntry class in the next example: DNSLookupResolver.
The Dns class is able to communicate with your default DNS server to retrieve IP addresses. The two important (static) methods are Resolve (), which uses the DNS server to obtain the details of a host with a given host name, and GetHostByAddress (), which also returns details of the host but this time using the IP address. Both methods return an IPHostEntry object:
IPHostEntry wroxHost = Dns.Resolve(·www.csharpaid.com·);
IPHostEntry wroxHostCopy = Dns.GetHostByAddress(·20S.215.179.17S·);
In this code, both IPHostEntry objects will contain details of the csharpaid.com servers.
The Dns class differs from the IPAddress and IPHostEntry classes because it has the ability to actually communicate with servers to obtain information. In contrast, IPAddress and IPHostEntry are more along the lines of simple data structures with convenient properties to allow access to the underlying data.
The DnsLookup Example
The DNS and IP-related classes ate illustrated with an example that looks up DNS names: DnsLookup
(see Figure 41-10).
This sample application simply invites the user to type in a DNS name using the main text box. When the user clicks the Resolve button, the sample uses the Dns. Resolve () method to retrieve an IPHostEntry reference and display the host name and IPaddresses. Note how the host name displayed may be different from the name typed in.
The DnsLookup application is a standard C# Windows application. The controls are added as shown in Figure 41-10,giving them the names txtBoxlnput, btnResolve, txtBoxHostName, and listBoxIPs, respectively. Then, you simply add the following method to the Forml class as the event handler for the buttonResolve Click event:
Notice that in this code you are careful to trap any exceptions. An exception might occur if the user types an invalid DNS name or if the network is down.
After retrieving the IPHostEntry instance, you use the AddressList property to obtain an array containing the IP addresses, which y.o.uthen iterate through with a foreach loop. for each entry, you display the lP address as an integer and as a string, using the IPAddress. AddressFamily. ToString method.
This section briefly discusses some of th,e.NET classes used to communicate at a lower level.
Network communications work o~ several different levels. The classes you have ~n in this chapter so far work at the highest level: the level at which specific commands are processed. It is probably easiest to understand this concept if you think of file transfer using FTP. Although today’s GUI applications hide many of the FTP details, it was not so long ago when you executed FTP from a command-line prompt. In this environment, you explicitly typed commands to send to the server for downloading, uploading, and listing files.
FTP is not the only high-level protocol relying on textual commands. HTTP, SMTP, POP,and other protocols are based on a similar type of behavior. Again, many of the modem graphical tools hide the transmission of commands from the user, so you are generally not aware of them. For example, when you type a URL into a Web browser, and the Web request goes off to a server, the browser is actually sending a (plain-text) GET command to the server, which fulfills a similar purpose as the FTP get command. It can also send a POST command, which indicates that the browser has attached other data to the request.
These protocols, however, are not sufficient by themselves to achieve communication between computers. Even if both the client and the server understand, for example, the HTTP protocol, it will still not be possible for them to understand each other unless there is also agreement on exactly how to transmit the characters: What binary format will be used? Moreover, getting down to the lowest level, what voltages will be used to represent Os and Is in the binary data? Because there are so many items to configure and agree upon, developers and hardware engineers in the networking field often refer to a protocol stack. When you list all of the various protocols and mechanisms required for communication between two hosts, you create a protocol stack with high-level protocols on the top and low-level prcsecols on the bottom. This approach results in a modular and layered approach to achieving efficient communication.
Luckily, for most development work, you do not need to go far down the stack or work with voltage levels. If you are writing code that requires efficient communication between computers, then it’s not unusual to write code that works directly at the level of sending binary data packets between computers. This is the realm of protocols such as TCP,and Microsoft has supplied a number of classes that allow you to conveniently work with binary data at this level.
The System. Net. Sockets namespace contains the relevant classes. These classes, for example, allow you to directly send out TCP network requests or to listen to TCP network requests on a particular port. The following table explains the main classes.
The SmtpClient object allows you to send mail messages through the Simple Mail Transfer Protocol. A simple sample of using the SmtpClient object is illustrated here:
In its simplest form, you work from an instance of the SmtpClient object. In this case, the instantiation also provided the host of the SMTP server that is used to send the mail messages over the Internet. You could have also achieved the same task by using the Host property.
Once you have the SmtpClient in place, it is simply a matter of calling the Send () method and providing the From address, the To address, and the Subject, followed by the Body of the mail message.
In many cases, you will have mail messages that are more complex than this. To work with this possibility, you can also pass in a Mail Message object into the Send() method.
Using Mail Message allows you to really fine-tune how you build your mail messages. You are able to send HTML messages, add as many To and CC recipients as you wish, change the message priority, work with the message encodings, and-add attachments. The ability to add attachments is defined here in the following code snippet.
In this case, an Attachment object is created and added, using the Add() method, to the Mail Message object before the Send() method is called.
UsIng the TCP Classes
The Transmission Control Protocol (TCP) classes offer simple methods for connecting and sending data between two endpoints. An endpoint is the combination Of an lP address and a port number. “Existing protocols have well-defined port numbers, for example, HTTP uses port 80, whereas SMTP uses port 25. The Internet Assigned Number Authority, lANA, assigns port numbers to these well known services. Unless you are implementing a well-known service, you will want to select a port number above 1,024.
TCP traffic makes up the majority of traffic on the Internet today. TCP is often the protocol of choice because it offers guaranteed delivery, error correction, and buffering. The TcpClient class encapsulates a TCP connection and provides a number of properties to regulate the connection, including buffering, buffer size, and timeouts. Reading and writing is accomplished by requesting a NetworkStream object
via the GetStream() method.
The TcpListener class listens for incoming TCP connections with the Start() method. When a connection request arrives, you can use the AcceptSocket() method to return a socket for communication with the remote machine, or use the AcceptTcpClient() method to use a higher-level TcpClient object for communication. The easiest way to see how the TcpListener and TcpClient classes work together is to work through an example.
The TcpSend and TcpRecelve Examples
To demonstrate how these classes work, you need to build two applications. Figure 41-11 shows the first application, TcpSend. This application opens a TCP connection to a server and sends the C# source code for itself.
Once again, you create a C# Windows application. The form consists of two text boxes (txtHost and txtPort) for the host name and port, respectively, as well as a button (btnSend) to click and start a connection. First,you ensure that you include the relevant namespaces:
This example creates the TcpClient using a host name and a port number. Alternatively, if you have an instance of the IPEndPoint class, then you can pass the instance to the TcpClient constructor. Afterretrieving an instance of the NetworkStream class, you open the source code file and begin to read bytes. As with many of the binary streams, you need to check for the end of the stream by comparing the return value of the ReadByte() method to -1.After your loop has read all of the bytes and sent them along to the network stream, you must close all of the open files, connections, and streams.
On the other side of the connection, the TcpReceive application displays the received file after the transmission is finished (see Figure 41-12).
The form consists of a single TextBox control, named txtDisplay. The TcpReceive application uses a TcpListener to wait for the incoming connection. To prevent freezing the application interface,you uae a background thread to wait for and then read from the connection. Thus, you need to include the System. Threading namespace as well these other namespaces:
The remaining important code is this:
The thread begins execution in the Listen () method and allows you to make the blocking call to AcceptTcpClient() without halting the interface. Notice that the IP address (127 . 0.0.1) and the port number (2112) are hard-coded into the application, so you will need to enter the same port number from the client application.
You use the Tcpclient object returned by AcceptTcpClient () to open a new stream for reading. As with the earlier example, you create a StreamReader to convert the incoming network data into a string. Before you close the client and stop the listener, you update the form’s text box. You do not want to access the text box directly from your background thread, so you use the form’s Invoke () method with a delegate and pass the result string as the first element in an array of obj ect parameters. Invoke()
ensures that your call is correctly marshaled into the thread that owns the control handles in the user interface.
TCP versus UDP
The other protocol covered in this section is UDP (User Datagram Protocol). UDP is a simple protocol with few features and little overhead. Developers often use UDP in applications where the speed and performance requirements outweigh the reliability needs, for example, video streaming. In contrast, rep offers a number of features to confirm the delivery of data. rep provides error correction and re transmission in the case of lost or corrupted packets. Last, but hardly least, rep buffers incoming and outgoing data and also guarantees that a sequence of packets scrambled in transmission is reassembled before delivery to the application. Even with the extra overhead, rep is the most widely used protocol across the Internet because of its high reliability
The UDP Class
As you might expect, the UdpClient class features a smaller and simpler interface than TcpClient. This reflects the relatively simpler nature of the protocol: Although both rep and UDP classes use a socket underneath the covers, the UdpClient class does not contain a method to return a network stream for reading and writing. Instead, the member function Send() accepts an array of bytes as a parameter, and the Receive() function returns an array of bytes. Also, because UDP is a connectionless protocol, you can wait to specify the endpoint for the communication as a parameter to the Send () and Recieve()
methods, instead of specifying it earlier in a constructor or Connect () method. You can also change the endpoint on each subsequent send or receive.
The following code fragment uses the UdpClient class to send a message to an echo service. A server with an echo service running accepts TCP or UDP connections on port 7. The echo service simply echoes any data sent to the server back to the client. This service is useful for diagnostics and testing, although many system administrators disable echo services for security reasons:
You make heavy use of the Encoding. ASCII class to translate strings into arrays of byte and vice versa. Also note that you pass an IPEndPoint by reference into the Receive() method. ‘Because UDP is not a connection-oriented protocol, each call to Receive() might pick up data from a different endpoint, so Receive() populates this parameter with the IP address and port of the sending host.
Both UdpClient and TcpClient offer a layer of abstraction over the lowest of the low-level classes: the Socket.
The Socket Class
The Socket class offers the highest level of control in network programming. One of the easiest ways to demonstrate the class is to rewrite the TcpReceive application with the Socket class .The updated Listen() method is listed in this example:
The Socket class requires a few more lines of code to complete the same task. For starters, the constructor arguments need to specify an IP addressing scheme for a streaming socket with the TCP protocol. These arguments are just one of the many combinations available to the Socket class. The TcpClient class can configure these settings for you. You then bind the listener socket to a port and begin to listen for incoming connections. When an incoming request arrives, you can use the Accept ( )
method to create a new socket to handle the connection. You ultimately attach a StreamReader instance to the socket to read the incoming data, in much the same fashion as before.
The Socket class also contains a number of methods for asynchronously accepting, connecting, sending, and receiving. You can use these methods with callback delegates in the same way you used the asynchronous page requests with the WebRequest class. If you really need to dig into the internals of the socket, the GetSocketOption() and SetSocketOption() methods are available. These methods allow you to see and configure options, including timeout, time-to-live, and other low-level options. Next, this chapter looks at another example of using sockets.
Building a Server Console Application
Looking further into the Socket class, this next example will create a console application that acts as a server for incoming socket requests. From there, a second example will be created in parallel (another console application), ~ch sends a message to the server console application.
The first application you should build is the console application that acts as a server. This application will open a socket on a specific TCP port and list
This example sets up a socket using the Socket class. The socket created uses the TCP protocol and is set up to receive incoming messages from any IP address using port 2112. Values that come in through the open socket are written to the console screen. This consuming application will continue to receive bytes until the [FINAL] string is received. This [FINAL) string signifies the end of the incoming message, and
the message c.an then be interpreted . After the end of the message is received from a client, a reply message is sent to the same client. From there, the socket is closed using the Close () method, and the console application will continue to stay up until a new message received.
Building the Client Application
The next step is to build a client application that will send a message to the first console application. The client will be able to send any message that it wants to the server console application as long as it follows some rules that were established by this application. The first of these rules is that the server console application is listening only on a particular protocol. In the case of this server application, it is listening using the TCP protocol. The other rule is that the server application is listening only on a particular port – in this case, port 2112. The last rule is that in any message that is being sent, the last bits of the message need to end with the string [FINAL].
The following client console application follows all of these rules:
In this example, an IPEndPoint object is created using the IP address of localhost as well as using port 2112 as required by the server console application. In this case, a socket is created and the Connect () method is listed.After the socket is opened and connected to the server console application socket instance, a string of text is sent to the server application using the Send () method. Because the server application is going to return a message, the Receive () method is used to grab this message (placing it in a byte array). From there,the byte array is converted into a string and displayed in the console application before the socket is shut down.
Running this application will produce the results presented.
Reviewing the two console applications in the figure, you can see that the server application opens and awaits incoming messages. The incoming message is sent from the client application, and the string sent is then displayed by the server application. The server application waits for other messages to come in, even after the first message is received and displayed. You can see this for yourself by shutting down the client application and re-running the application. You will then see that the server application again displays the message received.