How to Use the socketasynceventargs class

Source: Internet
Author: User
Tags apm
Document directory
  • How to Use the socketasynceventargs class
How to Use the socketasynceventargs class

ByMarcos Hidalgo Nunes| 14 Jan 2008

An article about how to use the socketasynceventargs class.

  • Download client-4.09 KB
  • Download server-7.5 KB
Introduction

I was looking for a high performance code for a client socket. previusly, I wrote a code based on the traditional asynchronous programming model methods fromSocketClass (BeginSend,BeginReceive, And so on ). but it didn't fill the performance requirements I needed. then, I found the new model for event-based asynchronous operations (see "get connected with. net Framework 3.5 "in the September 2007 issue of The msdn magazine ).

Background

The asynchronous programming model (APM) is used widely in I/O-bound applications to achieve high performance, due to the role ction of blocking threads. APM is implemented in. NET framework since its first version, and is improving since then, using new techniques like lambda expressions from C #3.0. specifically for socket programming, the new model for APM delivers an easier coding, not to mention the performance benefits. it is done towards the use ofSocketAsyncEventArgsClass to keep the context between I/O operations, which reduces object allocation and the work of garbage collection.

TheSocketAsyncEventArgsClass is also available in the. NET Framework 2.0 SP1, and the code from this article was written using Microsoft Visual Studio. NET 2005.

Using the code

To begin withSocketAsyncEventArgsClass, I studied the example from msdn, but there was something missing:AsyncUserTokenClass. I understood the class shocould expose a propertySocket, Corresponding to the socket used to perform the I/O operations. Soon, I realized the class wasn' t necessary, since the propertyUserTokenIsObject, So it cocould accept anything. below shown are the modified methods to directly use the instance of a socket asUserToken.

// Process the accept for the socket listener.private void ProcessAccept(SocketAsyncEventArgs e){    if (e.BytesTransferred > 0)    {        Interlocked.Increment(ref numConnectedSockets);        Console.WriteLine("Client connection accepted. " +                 "There are {0} clients connected to the server",                numConnectedSockets);    }    // Get the socket for the accepted client    // connection and put it into the     // ReadEventArg object user token.    SocketAsyncEventArgs readEventArgs = readWritePool.Pop();    readEventArgs.UserToken = e.AcceptSocket;    // As soon as the client is connected,    // post a receive to the connection.    Boolean willRaiseEvent =       e.AcceptSocket.ReceiveAsync(readEventArgs);    if (!willRaiseEvent)    {        ProcessReceive(readEventArgs);    }    // Accept the next connection request.    StartAccept(e);}// This method is invoked when an asynchronous receive operation completes. // If the remote host closed the connection, then the socket is closed.// If data was received then the data is echoed back to the client.private void ProcessReceive(SocketAsyncEventArgs e){    // Check if the remote host closed the connection.    if (e.BytesTransferred > 0)    {        if (e.SocketError == SocketError.Success)        {            Socket s = e.UserToken as Socket;            Int32 bytesTransferred = e.BytesTransferred;            // Get the message received from the listener.            String received = Encoding.ASCII.GetString(e.Buffer,                               e.Offset, bytesTransferred);            // Increment the count of the total bytes receive by the server.            Interlocked.Add(ref totalBytesRead, bytesTransferred);            Console.WriteLine("Received: \"{0}\". The server has read" +                               " a total of {1} bytes.", received,                               totalBytesRead);            // Format the data to send to the client.            Byte[] sendBuffer =               Encoding.ASCII.GetBytes("Returning " + received);            // Set the buffer to send back to the client.            e.SetBuffer(sendBuffer, 0, sendBuffer.Length);            Boolean willRaiseEvent = s.SendAsync(e);            if (!willRaiseEvent)            {                ProcessSend(e);            }        }        else        {            CloseClientSocket(e);        }    }}// This method is invoked when an asynchronous send operation completes.// The method issues another receive on the socket to read any additional // data sent from the client.private void ProcessSend(SocketAsyncEventArgs e){    if (e.SocketError == SocketError.Success)    {        // Done echoing data back to the client.        Socket s = e.UserToken as Socket;        // Read the next block of data send from the client.        Boolean willRaiseEvent = s.ReceiveAsync(e);        if (!willRaiseEvent)        {            ProcessReceive(e);        }    }    else    {        CloseClientSocket(e);    }}

I also modified the code to show how to manipulate the message received ed by the listener, instead of simply echoing back to the client, as seen inProcessReceiveMethod. In the example, I used the propertiesBuffer,Offset, AndBytesTransferedTo get the received message, andSetBufferMethod to put the modified answer to the client.

To control the listener lifetime, is used an instance ofMutexClass.StartMethod, which is a based on the originalInitMethod, creates the mutex, and the correspondingStopMethod releases the mutex. These methods are suitable to implement the socket server as a Windows service.

// Starts the server such that it is listening// for incoming connection requests.internal void Start(Object data){    Int32 port = (Int32)data;    // Get host related information.    IPAddress[] addressList =           Dns.GetHostEntry(Environment.MachineName).AddressList;    // Get endpoint for the listener.    IPEndPoint localEndPoint =           new IPEndPoint(addressList[addressList.Length - 1], port);    // Create the socket which listens for incoming connections.    this.listenSocket = new Socket(localEndPoint.AddressFamily,                         SocketType.Stream, ProtocolType.Tcp);    if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)    {        // Set dual-mode (IPv4 & IPv6) for the socket listener.        // 27 is equivalent to IPV6_V6ONLY socket        // option in the winsock snippet below,        // based on http://blogs.msdn.com/wndp/archive/2006/10/24/        //   creating-ip-agnostic-applications-part-2-dual-mode-sockets.aspx        this.listenSocket.SetSocketOption(SocketOptionLevel.IPv6,                                          (SocketOptionName)27, false);        this.listenSocket.Bind(new IPEndPoint(IPAddress.IPv6Any,                                localEndPoint.Port));    }    else    {        // Associate the socket with the local endpoint.        this.listenSocket.Bind(localEndPoint);    }    // Start the server with a listen backlog of 100 connections.    this.listenSocket.Listen(100);    // Post accepts on the listening socket.    this.StartAccept(null);    mutex.WaitOne();}// Stop the server.internal void Stop(){    mutex.ReleaseMutex();}

Now that we have a socket server, the next step is to create a socket client usingSocketAsyncEventArgsClass. Although the msdn says the class is specifically designed for network server applications, there is no restriction in using this APM in a client code. below, there isSocketClienClass, written in this way:

using System;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading;namespace SocketAsyncClient{    // Implements the connection logic for the socket client.    internal sealed class SocketClient : IDisposable    {        // Constants for socket operations.        private const Int32 ReceiveOperation = 1, SendOperation = 0;        // The socket used to send/receive messages.        private Socket clientSocket;        // Flag for connected socket.        private Boolean connected = false;        // Listener endpoint.        private IPEndPoint hostEndPoint;        // Signals a connection.        private static AutoResetEvent autoConnectEvent =                               new AutoResetEvent(false);         // Signals the send/receive operation.        private static AutoResetEvent[]                 autoSendReceiveEvents = new AutoResetEvent[]        {            new AutoResetEvent(false),            new AutoResetEvent(false)        };        // Create an uninitialized client instance.        // To start the send/receive processing call the        // Connect method followed by SendReceive method.        internal SocketClient(String hostName, Int32 port)        {            // Get host related information.            IPHostEntry host = Dns.GetHostEntry(hostName);            // Addres of the host.            IPAddress[] addressList = host.AddressList;            // Instantiates the endpoint and socket.            hostEndPoint =               new IPEndPoint(addressList[addressList.Length - 1], port);            clientSocket = new Socket(hostEndPoint.AddressFamily,                                SocketType.Stream, ProtocolType.Tcp);        }        // Connect to the host.        internal void Connect()        {            SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();            connectArgs.UserToken = clientSocket;            connectArgs.RemoteEndPoint = hostEndPoint;            connectArgs.Completed +=                new EventHandler<socketasynceventargs />(OnConnect);            clientSocket.ConnectAsync(connectArgs);            autoConnectEvent.WaitOne();            SocketError errorCode = connectArgs.SocketError;            if (errorCode != SocketError.Success)            {                throw new SocketException((Int32)errorCode);            }        }        /// Disconnect from the host.        internal void Disconnect()        {            clientSocket.Disconnect(false);        }        // Calback for connect operation        private void OnConnect(object sender, SocketAsyncEventArgs e)        {            // Signals the end of connection.            autoConnectEvent.Set();            // Set the flag for socket connected.            connected = (e.SocketError == SocketError.Success);        }        // Calback for receive operation        private void OnReceive(object sender, SocketAsyncEventArgs e)        {            // Signals the end of receive.            autoSendReceiveEvents[SendOperation].Set();        }        // Calback for send operation        private void OnSend(object sender, SocketAsyncEventArgs e)        {            // Signals the end of send.            autoSendReceiveEvents[ReceiveOperation].Set();            if (e.SocketError == SocketError.Success)            {                if (e.LastOperation == SocketAsyncOperation.Send)                {                    // Prepare receiving.                    Socket s = e.UserToken as Socket;                    byte[] receiveBuffer = new byte[255];                    e.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);                    e.Completed +=                       new EventHandler<socketasynceventargs />(OnReceive);                    s.ReceiveAsync(e);                }            }            else            {                ProcessError(e);            }        }        // Close socket in case of failure and throws        // a SockeException according to the SocketError.        private void ProcessError(SocketAsyncEventArgs e)        {            Socket s = e.UserToken as Socket;            if (s.Connected)            {                // close the socket associated with the client                try                {                    s.Shutdown(SocketShutdown.Both);                }                catch (Exception)                {                    // throws if client process has already closed                }                finally                {                    if (s.Connected)                    {                        s.Close();                    }                }            }            // Throw the SocketException            throw new SocketException((Int32)e.SocketError);        }        // Exchange a message with the host.        internal String SendReceive(String message)        {            if (connected)            {                // Create a buffer to send.                Byte[] sendBuffer = Encoding.ASCII.GetBytes(message);                // Prepare arguments for send/receive operation.                SocketAsyncEventArgs completeArgs = new SocketAsyncEventArgs();                completeArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);                completeArgs.UserToken = clientSocket;                completeArgs.RemoteEndPoint = hostEndPoint;                completeArgs.Completed +=                   new EventHandler<socketasynceventargs />(OnSend);                // Start sending asyncronally.                clientSocket.SendAsync(completeArgs);                // Wait for the send/receive completed.                AutoResetEvent.WaitAll(autoSendReceiveEvents);                // Return data from SocketAsyncEventArgs buffer.                return Encoding.ASCII.GetString(completeArgs.Buffer,                        completeArgs.Offset, completeArgs.BytesTransferred);            }            else            {                throw new SocketException((Int32)SocketError.NotConnected);            }        }        #region IDisposable Members        // Disposes the instance of SocketClient.        public void Dispose()        {            autoConnectEvent.Close();            autoSendReceiveEvents[SendOperation].Close();            autoSendReceiveEvents[ReceiveOperation].Close();            if (clientSocket.Connected)            {                clientSocket.Close();            }        }        #endregion    }}
Points of interest

I had an experience with a socket server running in a clustered environment. In this scenario, you can't use the first entry in the address list from the host. Instead, you should useLastAddress, as shown inStartMethod. another technique presented here is how to set the dual mode for an ip6 address family, which is helpful if you want to run the server in a Windows Vista or Windows Server 2008, which enables ip6 by default.

Both programs use command-line arguments to run. in the client example, you shoshould inform "localhost" as the name of the host instead of the machine name if both the server and the client are running in a machine out of a Windows domain.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.