Asynchronous Methods

In document C# Network Programming (Page 171-177)

The regular Dns methods might cause a problem for your C# program because they use blocking mode to communicate with the remote DNS server. If you ran the example programs offered earlier in this section on the DNS classes, you may have noticed that it often takes a few seconds for the DNS information to be returned from the remote DNS server. This may not seem like much of a problem, but it can cause serious difficulties in Windows programs that allow the user to do other things at the same time. While the program is waiting for the DNS server response, the user is prevented from clicking any buttons or entering any information in TextBox fields. For some applications, this can mean unacceptable performance.

Chapter 3, C# Network Programming Classes, describes techniques that can be used to prevent network blocking calls. One of the techniques is to use the asynchronous socket methods (described in more detail in Chapter 8, Asynchronous Socket Programming). These methods allow you to start a network function and specify a delegate to call when the network function completes. Meanwhile, the rest of the program can go happily on its way doing other things. When the network function is completed, it signals the program to run the delegate method.

The Dns class provides the following asynchronous methods to allow your programs to perform asynchronous DNS

function calls:

BeginGetHostByName()

BeginResolve()

EndGetHostByName()

EndResolve()

Each of the asynchronous methods parallels the equivalent synchronous method. At this time there is no asynchronous GetHostByAddress() call.

The methods are called in pairs. The Beginxxx method is called from the program and points to a delegate method, which contains the Endxxx method. For example, BeginResolve() uses the following format:

public static IAsyncResult BeginResolve(string hostname, AsyncCallback requestCallback, object stateObject)

The method uses three parameters:

A string representation, hostname, of a hostname or IP address

An AsyncCallback object, requestCallback, which defines the delegate

A generic object, stateObject, which defines the method state

The AsyncCallback object is created to point to the delegate method used when the DNS results are returned. To do this, you must first create the AsyncCallback object, then assign the delegate method to it:

private AsyncCallback OnResolved;

OnResolved = new AsyncCallback(Resolved);

Object state = new Object();

. .

Dns.BeginResolve(addr, OnResolved, state);

. }

private void Resolved(IasyncResult ar) {

IPHostEntry iphe = Dns.EndResolve(ar);

}

The OnResolved AsyncCallback object points to the delegate method Resolved, which will contain the EndResolve() method to complete the DNS call. The state object allows you to pass information to the delegate method. The EndResolve() method has this format:

public static IPHostEntry EndResolve(IasyncResult ar)

The IAsyncResult object ar is what refers the EndResolve() method to the original BeginResolve() method. The result of the EndResolve() method is the standard IPHostEntry object, which can then be broken down to extract the returned information, similar to the standard Resolve() method discussed earlier.

Listing 4.8 shows a sample program that uses the BeginResolve() and EndResolve() methods in a Windows Forms environment.

Listing 4.8: The AsyncResolve.cs program

using System;

using System.Drawing;

using System.Net;

using System.Text;

using System.Windows.Forms;

class AsyncResolve Form:

{

TextBox address;

ListBox results;

private AsyncCallback OnResolved;

public AsyncResolve() {

Text = "DNS Address Resolver";

Size = new Size(400,380);

OnResolved = new AsyncCallback(Resolved);

Label label1 = new Label();

label1.Parent = this;

label1.Text = "Enter address to resolve:";

label1.AutoSize = true;

label1.Location = new Point(10, 10);

address = new TextBox();

address.Parent = this;

address.Size = new Size(200, 2 * Font.Height);

address.Location = new Point(10, 35);

results = new ListBox();

results.Parent = this;

results.Location = new Point(10, 65);

results.Size = new Size(350, 20 * Font.Height);

Button checkit = new Button();

checkit.Parent = this;

checkit.Text = "Resolve";

checkit.Location = new Point(235,32);

checkit.Size = new Size(7 * Font.Height, 2 * Font.Height);

checkit.Click += new EventHandler(ButtonResolveOnClick);

}

void ButtonResolveOnClick(object obj, EventArgs ea) {

results.Items.Clear();

string addr = address.Text;

Object state = new Object();

Dns.BeginResolve(addr, OnResolved, state);

}

private void Resolved(IAsyncResult ar) {

string buffer;

IPHostEntry iphe = Dns.EndResolve(ar);

buffer = "Host name: " + iphe.HostName;

results.Items.Add(buffer);

foreach(string alias in iphe.Aliases) {

buffer = "Alias: " + alias;

results.Items.Add(buffer);

}

foreach(IPAddress addrs in iphe.AddressList) {

buffer = "Address: " + addrs.ToString();

results.Items.Add(buffer);

} }

public static void Main() {

Application.Run(new AsyncResolve());

} }

Because Windows Forms programs operate in a graphical environment, they work more efficiently using

asynchronous Dns methods rather than the synchronous methods. The first part of the AsyncResolve.cs program defines the constructor for the class. The constructor defines a standard Windows Form and adds four controls:

A Label object to display some instructional text

A TextBox object to allow the customer to enter the address to resolve

A ListBox object to easily return the output of the DNS query to the customer

A Button object to allow the customer to start the DNS query

After the constructor, two methods are defined. The ButtonResolveOnClick() method is registered as a delegate for the button using the Click property:

checkit.Click += new EventHandler(ButtonResolveOnClick);

Notice that the EventHandler object is added to the existing list of event handlers for the control. The EventHandler object references the delegate to call when the button Click event is detected.

When the Button object is clicked, the ButtonResolveOnClick() delegate is performed. It reads the value entered into the TextBox object and calls the BeginResolve() method using the value of the object, the AsyncCallback object created earlier, and an empty state object:

Dns.BeginResolve(addr, OnResolved, state);

The OnResolved parameter is defined as an AsyncCallback object, and defines the Resolved() method in the program class:

private AsyncCallback OnResolved = null;

OnResolved = new AsyncCallback(Resolved);

The Resolved() method calls the EndResolve() Dns class method, which completes the BeginResolve/EndResolve pair. It must use the IasyncResult object parameter as the parameter value:

private void Resolved(IAsyncResult ar) {

IPHostEntry iphe = Dns.EndResolve(ar);

The IPHostEntry object is then dissected using the standard methods used in the other DNS programs in this chapter, to find the hostname, any aliases, and any IP addresses returned from the DNS query. To make life a little simpler, the results are easily written to a ListBox object. Figure 4.6 shows a sample output from the program.

Figure 4.6: AsyncResult program results, written to a ListBox object

The AsyncResult program can show results from both hostname and IP address queries. Each time another address is queried, the ListBox is cleared to make it easier for the new information to be read. If you prefer to keep a log of all the queries processed, you can remove the following line from the ButtonResolveOnClick() method:

results.Items.Clear();

If you do this, you might also want to add some type of separator line after the DNS query information is written to the ListBox, to help distinguish between queries. Here s an example:

results.Items.Add(" ");

When the query log is too long for the ListBox, a vertical scrollbar will appear, allowing you to scroll through the entire query log. Pretty neat payoff for a short amount of code!

Summary

The Domain Name System (DNS) can be used by C# programs to determine the IP addresses of hostnames. DNS is a distributed hierarchical database system that assigns hostnames hierarchically, separating each level with a period.

DNS servers are created to store the DNS database for individual domains. Clients can query the DNS servers to find the IP address associated with a hostname, or other domain information. If a local DNS server cannot answer the query, it goes to a higher-level DNS server until the query can be answered and returned to the client.

Windows systems can resolve hostnames in two ways. The first way is to use the hosts file to manually enter hostnames and provide quick hostname resolution. The second way is to configure DNS servers in the system to contact remote DNS servers and resolve DNS information. The C# language provides the RegistryKey class to query the local Registry for the addresses of DNS servers configured on the system.

Windows systems also include the nslookup program, which lets customers do manual DNS queries. The nslookup program has several options for altering the way it finds and displays DNS information. The debug option allows you to see the actual DNS query sent to the remote DNS server. The querytype option allows you to query for specific DNS record information for a domain.

C# contains the Dns class in the System.Net namespace to provide basic DNS query capability. The

GetHostByName() and GetHostByAddress() methods allow C# programs to determine DNS information given particular host data. The Resolve() method can find DNS information for either a hostname or an IP address.

Also covered in this chapter are asynchronous Dns methods, for doing DNS queries in applications that don t allow you to tie up system resources while waiting for an answer from the DNS server. The asynchronous methods use the standard C# delegate system to call a method when the DNS query has returned. This can greatly improve the performance of Windows-based network programs

Part II: Network Layer

In document C# Network Programming (Page 171-177)