Jump to content

The asynchronous Socket

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
1 reply to this topic

#1
Scott

Scott

    Newbie

  • Members
  • Pip
  • 4 posts
In this post, we’ll look at asynchronous sockets in great detail. We’ll begin with a brief description of the Windows programming environment. You’ll see how Windows event programming changes the way network programs operate. We’ll discuss two ways to avoid using blocking sockets in your network applications:

  • Using .NET asynchronous sockets

  • Using traditional non-blocking socket methods

Each of these models has its own set of pros and cons to consider in terms of a Windows network application. The following sections describe the methods and processes used in both models, along with tips for employing them within your C# network programs.

Why asynchronous sockets?:sneaky:

In structured programming, program flow is controlled within the program itself. Each method within the class is called in turn by the functions as they occur in the program execution. The customer does not have options for changing the program execution other than what is allowed by the program. By contrast, Windows programming uses an event programming model.

Windows event programming bases program flow on events. As events occur within the program, specific methods are called and performed based on the events, as illustrated in the image below. This does not work well with blocking network functions, however. When an application presents a graphical interface to a customer, it will wait for events from the customer to determine what functions to perform. Event programming assumes that while other functions are processing (such as network access), the customer will still have control over the graphical interface. This allows the customer to perform other functions while waiting for a network response, or even abort network connections if necessary. However, if the blocking network functions were used, program execution would wait while the function was performed, and the customer would have control over neither the interface nor the program.

Posted Image

This section describes how Windows event programming is accomplished using C# constructs and how the .NET asynchronous network methods work within the Windows event programming model.

Using Events and Delegates

The .NET programming environment is closely tied to the Windows environment, so it is not surprising that .NET fully supports the event programming model. In .NET event programming, the two key constructs are the events and delegates.

An event is a message sent by an object that represents an action that has taken place. The message identifies the action and gives any useful data related to the action. Events can be anything from the customer clicking a button (where the message represents the button name), to a packet being received on a socket (where the message represents the socket that received the data). The event sender does not necessarily know what object will handle the event message once it is sent through the Windows system. It is up to the event receiver to register with the Windows system and inform it of what types of events the receiver wants to receive. The image below demonstrates this function.

Posted Image

The event receiver is identified within the Windows system by a pointer class called a delegate. The delegate is a class that holds a reference to a method that can handle the received event. When Windows receives an event, it checks to see if any delegates are registered to handle it. If any delegates are registered to handle the event, the event message is passed to the methods defined by the delegates. After the methods complete, the Windows system processes the next event that occurs, until an event signals the end of the program.

Sample Event Program

Successful event programming is, of course, vital to writing successful Windows programs. Every object produced in a Windows graphical program can generate one or more events based on what the customer is doing with the object. The .NET Framework System.Windows.Forms namespace contains classes for all the Windows objects necessary to create full-featured graphical programs in the Windows environment. These include the following:

  • Buttons

  • Text boxes

  • List boxes

  • Combo boxes

  • Check boxes

  • Text labels

  • Scroll bars

  • Window menus

It’s easy to create professional-quality network programs using these Windows objects. As each object is added to a Window form, you must register the method that will be used for its event handler. When the event is generated, Windows passes control of the program to the event handler method. Listing 1.0 demonstrates a simple Windows Forms program that uses some simple Windows objects.

Listing 1.0: The WindowSample.cs program
using System;

using System.Drawing;

using System.Windows.Forms;

class WindowSample    Form:

{

  private TextBox data;

  private ListBox results;

  public WindowSample()

  {

   Text = "Sample Window Program";

   Size = new Size(400, 380);

   Label label1 = new Label();

   label1.Parent = this;

   label1.Text = "Enter text string:";

   label1.AutoSize = true;

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

   data = new TextBox();

   data.Parent = this;

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

   data.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 = "test";

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

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

   checkit.Click += new

      EventHandler(checkit_OnClick);

  }

  void checkit_OnClick(object obj, EventArgs ea)

  {

   results.Items.Add(data.Text);

   data.Clear();

  }

  public static void Main()

  {

   Application.Run(new WindowSample());

  }

}


This sample program is pretty simplistic from the Forms point of view so you can focus on what it teaches you about event programming. First, remember that all Windows Forms programs must use the System.Windows.Forms namespace, along with the System.Drawing namespace, to help position objects in the window:

using System.Drawing;
using System.Windows.Forms;
Because the application creates a window, it must inherit the standard window Form class:

class WindowSample : Form
The constructor for the class must define all the graphical objects that are used in the form. First, the standard values for the Windows header and default size are defined:

Text = "Sample Window Program";
Size = new Size(400, 380);
Next, each object that will appear in the window is defined, along with its own properties. This example creates the following objects:

  • A Label object to display an instructional text string

  • A TextBox object to allow the customer to enter data

  • A ListBox object to easily display output to the customer

  • A Button object to allow the customer to control when the action will occur
The key to the action in this program is the EventHandler registered for the Button object, which registers the method ButtonOnClick() with a click event on the Button object checkit:

checkit.Click += new EventHandler(ButtonOnClick);

When the customer clicks the button, the program control moves to the ButtonOnClick() method:

void ButtonOnClick(object obj, EventArgs ea)
{
results.Items.Add(data.Text);
data.Clear();
}
This simple method performs only two functions. First it extracts the text string entered in the TextBox object and writes it to the ListBox object. Next, it clears the text in the TextBox. Each time the customer clicks the Button object, a new text string is placed in the ListBox as a new line. These simple Windows Forms programming objects will be utilized in a network programming example in the later on.

Note When compiling a Windows Forms program, you should use the csc command compiler /t:winexe option, as follows: csc /t:winexe WindowSample.cs. This creates a Windows executable program, which does not needlessly open a command prompt window when it is run.



#2
Scott

Scott

    Newbie

  • Members
  • Pip
  • 4 posts
The AsyncCallback Class

Just as events can trigger delegates, .NET also provides a way for methods to trigger delegates. The .NET AsyncCallback class allows methods to start an asynchronous function and supply a delegate method to call when the asynchronous function completes.

This process is different from standard event programming in that the event is not generated from a Windows object, but rather from another method in the program. This method itself registers an AsyncCallback delegate to call when the method completes its function. As soon as this occurs and the method indicates its completion to the Windows OS, an event is triggered to transfer the program control to the method defined in the registered AsyncCallback delegate.

The Socket class utilizes the method defined in the AsyncCallback to allow network functions to operate asynchronously in background processing. It signals the OS when the network functions have completed and passes program control to the AsyncCallback method to finish the network function. In a Windows programming environment, these methods often help avoid the occurrence of an application lock-up while waiting for network functions to complete.

Using Asynchronous Sockets

The Socket object contains methods that utilize the AsyncCallback class to call completion methods when the network functions are finished. This allows the application to continue processing other events while waiting for network operations to complete their work.

  • The Socket asynchronous methods split common network programming functions into two pieces:

  • A Begin method that starts the network function and registers the AsyncCallback method

  • An End method that completes the function when the AsyncCallback method is called

Table 1.0 shows the asynchronous methods that are available to use with Socket objects. Each Begin method has an associated End method to complete the function.

Table 1.0: .Net asynchronous Socket methods


Posted Image

Note Notice that these methods apply only to Socket objects. In .NET Framework release 1, the TcpClient, TcpListener, and UdpClient classes do not include asynchronous methods.



In this section, we’ll walk through the process of using each of the asynchronous Socket methods to process network functions. Then we’ll look at a couple of sample programs that put it all together.

Establishing the Connection

The method used to establish a connection with a remote host depends on whether the program is acting as a server (waiting for clients to connect to it) or a client (attempting to connect to a remote server). For servers, the BeginAccept() method should be used; for clients, the BeginConnect() method is used.

The BeginAccept() and EndAccept() Methods
To accept an incoming connection attempt from a remote client, you must use the BeginAccept() method. Its format is as follows:

IAsyncResult BeginAccept(AsyncCallback callback, object state)
The BeginAccept() method takes two parameters: the name of the AsyncCallback method used to complete the function, and a generic state object that can pass information between the asynchronous methods.

This is how the BeginAccept() method is typically used:

Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
sock.Bind(iep);
sock.Listen(5);
sock.BeginAccept(new AsyncCallback(CallAccept), sock);
This code snippet creates a Socket object and assigns it to a local IP address and TCP port to listen for incoming connections. The BeginAccpet() method defines the method used as the delegate to be used when a connection attempt is detected on the socket. The last parameter passed to the BeginAccept() method is the original Socket object created.

After the BeginAccept() method is finished, the AsyncCallback method defined will be called when a connection attempt occurs. The AsyncCallback method must include the EndAccept() method to finish the socket accept. Here is the format of the EndAccept() method:

Socket EndAccept(IAsyncResult iar);
The IAsyncResult object parameter passes the IAsyncResult value from the associated BeginAccept() method to the EndAccept() method—this is how the BeginAccept() and EndAccept() pairs are matched.

Similar to the synchronous Accept() method, the EndAccept() method returns a Socket object that is used for the new connection with the client. All further communication with the remote client should be done using this Socket object. A sample section from an EndAccept() AsyncCallback method would look like this:

private static void CallAccept(IAsyncResult iar)
{
Socket server = (Socket)iar.AsyncState;
Socket client = server.EndAccept(iar);
.
.
.
}

The name of the AsyncCallback method must match the name used in the BeginAccept() method parameter. The first step in the method is to retrieve the original server socket. This is done using the AsyncState property of the IAsyncResult class. This property passes the original object placed in the BeginAccept() object parameter. Because it is defined as a generic object, it must be typecast into a Socket object.

After the original Socket object is retrieved, the EndAccept() method can obtain a new Socket object for the client connection. The IAsyncResult object parameter should be the same as that passed to the AsyncCallback method.

The client Socket object, once created, can be used just like any other Socket object, using either synchronous or asynchronous methods to read and write data to the socket.

In the end, the BeginAccept()/EndAccept() asynchronous pair produce the same results as the Accept() synchronous method. You will find that this is true of all the asynchronous Begin() and End() methods. This allows them to be used as direct replacements for the synchronous method calls in network programs that need asynchronous behavior.

The BeginConnect() and EndConnect() Methods

For a client application to connect to a remote server using asynchronous methods, you must use the BeginConnect() method. Its format is as follows:

IAsyncResult BeginConnect(EndPoint ep, AsyncCallback callback, Object state)
The first parameter passed to the BeginConnect() method is the EndPoint value of the remote host to connect to. Like the BeginAccept() method, the BeginConnect() method specifies the AsyncCallback method name of the delegate method to call when the connection is ready for completion. The last parameter is a state object that can be passed to the EndConnect() method to transfer necessary data.

Here is an example of BeginConnect() code:

Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);
This code snippet creates a Socket object newsock and an IPEndPoint object iep for the remote host. The BeginConnect() method references the AsyncCallback method (Connected) and passes the original Socket object newsock to the AsyncCallback method.

When the connection is completed, the AsyncCallback method that was declared is called. The AsyncCallback method uses the EndConnect() method to complete the connection.

The format of the EndConnect() method is as follows:

EndConnect(IAsyncResult iar)

Here again, the IAsyncResult object specified pass the object value from the BeginConnect() method. It will be instantiated with the IAsyncResult object returned by the BeginConnect() method. A sample method to use this would look like the following:

public static void Connected(IAsyncResult iar)
{
Socket sock = (Socket)iar.AsyncState;
try
{
sock.EndConnect(iar);
} catch (SocketException)
{
Console.WriteLine("Unable to connect to host");
}
}
The first statement in the AsyncCallback method retrieves the original socket used for the BeginConnect() method call, using the AsyncState property of the IAsyncResult object passed to the AsyncCallback method.

After the original socket is recreated, the EndConnect() method is called, using the IAsyncResult object to point it back to the original BeginConnect() method. Because it is possible that the remote host will not be available, it is good practice to place the EndConnect() method in a try-catch block. If the EndConnect() method is unsuccessful, it will throw a SocketException error.

Sending and Receiving Data

After a connection is established, you will most likely want to send and receive data with the remote host. Asynchronous methods can also be used to do this.

The BeginSend() and EndSend() Methods

The BeginSend() method sends data to a connected socket. The format of this method is as follows:

IAsyncResult BeginSend(byte[] buffer, int offset, int size,
SocketFlags sockflag, AsyncCallback callback, object state)
As you can see, there are plenty of parameters to use with the BeginSend() method. You may notice that most of them are similar to the synchronous Send() method. Walking through them, the first one is the byte array that contains the data you want sent. The offset parameter points to the location within the buffer parameter from which to start sending data, and the size parameter specifies how many bytes from the buffer parameter to send. Next, the sockflag parameter specifies any special socket flags that you want set for the communication. Finally, the AsyncCallback callback and state objects are specified, defining the method to call when the BeginSend() method succeeds and a state object to send information to the EndSend() method.

A sample BeginSend() method would look like this:

sock.BeginSend(data, 0, data.Length, SocketFlags.None,
new AsyncCallback(SendData), sock);
This example sends the entire data buffer and calls the SendData() method when the socket is ready to send the data. The sock Socket object is passed to the AsynCallback() method.

The EndSend() method completes the sending of the data. The format for this method is as follows, where the IAsyncResult parameter defines an empty object that references the result of the BeginSend() method call:

int EndSend(IAsyncResult iar)
The EndSend() method returns the number of bytes successfully sent from the socket.

Here’s an example of the EndSend() AsyncCallback method:

private static void SendData(IAsyncResult iar)
{
Socket server = (Socket)iar.AsyncState;
int sent = server.EndSend(iar);
}
The original sock socket is re-created using the AsyncState property of the IAsyncResult object passed to the AsyncCallback method. Again, this relates to the state object specified in the BeginSend() method.

The BeginSendTo() and EndSendTo() Methods

The BeginSendTo() method is used with connectionless sockets to start an asynchronous data transmission to a remote host. The format of the BeginSendTo() method is as follows:

IAsyncResult BeginSendTo(byte[] buffer, int offset, int size,
SocketFlags sockflag, EndPoint ep, AsyncCallback callback, object state)
As you can see, for the most part the BeginSendTo() method format is similar to the SendTo() method format, with the asynchronous features of the BeginSend() method added. The BeginSendTo() method allows you to specify the EndPoint object of the remote host to which to send the message.

Sample sBeginSendTo() code would look like this:

IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.1.6"), 9050);
sock.BeginSendTo(data, 0, data.Length, SocketFlags.None, iep,
new AsynCallback(SendDataTo), sock);

Again, the only difference between this call and the BeginSend() call is the IPEndPoint of the remote host added to the parameters.

The EndSendTo() method uses the standard format of End methods:

int EndSendTo(IAsyncResult iar)
As always, the IAsyncResult parameter accepts the IAsyncResult value returned by the BeginSendTo() method. The EndSendTo() method returns the number of bytes sent out from the socket.

The BeginReceive() and EndReceive() Methods

The BeginReceive() method accepts data from a remote host on a socket. The format for this method is as follows:

IAsyncResult BeginReceive(byte[] buffer, int offset, int size,
SocketFlags sockflag, AsyncCallback callback, object state)
The first parameter is a byte array that accepts the incoming data. The offset and size parameters are used to specify where in the buffer to start placing the data, and how large the buffer is. The sockflags parameter sets any needed socket flags for the communication. The callback and state parameters allows you to pass information to the EndReceive() method.

Here’s a sample BeginReceive() method call:

sock.BeginReceive(data, 0, data.Length, SocketFlags.None,
new AsyncCallback(ReceivedData), sock);
The BeginReceive() method passes the original socket to the EndReceive() method so that it can re-create the socket for the AsyncCallback method. The AsyncCallback method used for the EndReceive() method would look like this:

void ReceivedData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int recv = remote.EndReceive(iar);
string receivedData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(stringData);
}
You see the same AsyncCallback behavior again: the first statement recreates the original socket from the IAsyncResult object. The second statement uses the socket to finish the socket receive, via the EndReceive() method call. Incoming data is placed in the data buffer specified in the corresponding BeginReceive() method call. To access the data buffer from the AsyncCallback method, you must ensure that it is accessible from both methods (such as using a global variable or data class).

The BeginReceiveFrom() and EndReceiveFrom() Methods

The BeginReceiveFrom() method accepts data from any remote host on a connectionless socket. Here is the format:

IAsyncResult BeginReceiveFrom(byte[] buffer, int offset, int size,
SocketFlags sockflag, ref EndPoint ep, AsyncCallback callback, object state)
Notice that the BeginReceiveFrom() method is similar to the BeginReceive() method, except that it specifies a reference to an EndPoint object. The EndPoint object defines the remote host IP address and port number that sent the data.

Warning Remember that the BeginReceiveFrom() method uses a reference to an EndPoint object, not the object itself. It’s easy to forget that ref keyword and thus have a problem on your hands.


A sample BeginReceiveFrom() method would look like this:

sock.BeginReceive(data, 0, data.Length, SocketFlags.None, ref iep, new AsyncCallback(ReceiveData), sock);
The corresponding EndReceiveFrom() method is placed in the appropriate AsyncCallback method:

void ReceiveData(IasyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int recv = remote.EndReceiveFrom(iar);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(stringData);
}
The EndReceiveFrom() method returns the number of bytes read from the socket and places the received data in the data buffer defined in the BeginReceiveFrom() method. Again, if you need to access this data within the AsyncCallback method, you should ensure that the data buffer is accessible from both methods.

TO BE CONTINUED. . .

Edited by Scott, 07 May 2010 - 11:53 AM.