Saturday, May 01, 2010

Using Asynchronous Sockets

the following have been taken from this url: http://codeidol.com/csharp/csharp-network/Asynchronous-Sockets/Using-Asynchronous-Sockets

an additional article is this one: http://www.codeguru.com/csharp/csharp/cs_misc/sampleprograms/print.php/c7695/%5C%5Cwww.vt.edu

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 8.1 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 8.1: .Net asynchronous Socket methods Requests Started By…
Description of Request
Requests Ended BY…

BeginAccept()
To accept an incoming connection
EndAccept()

BeginConnect()
To connect to a remote host
EndConnect()

BeginReceive()
To retrieve data from a socket
EndReceive()

BeginReceiveFrom()
To retrieve data from a specific remote host
EndReceiveFrom()

BeginSend()
To send data from a socket
EndSend()

BeginSendTo()
To send data to a specific remote host
EndSendTo()

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.

No comments:

Shared Cache - .Net Caching made easy

All information about Shared Cache is available here: http://www.sharedcache.com/. Its free and easy to use, we provide all sources at codeplex.

Facebook Badge