SocketAyncEventAgs類の使用と詳細について

40490 ワード

Download client-4.1 KB Download server-7.29 KB (1)開くhttp://msdn.microsoft.com/library/(2)MSDNのsearchの入力ボックスにキーワード「SocketAryncEventAgs」を入力します.
(3)How To Use the SocketAyncEventAgs Class-CodeProject
An articale about how to use the
SocketAyncEventAgs class;Author:Marcos Hdalgo Nunes;Updated:29 Sep 2010;Section:Internet/Network;Chapter:General…
wwww.co.co/…/How-to-Use-the-
SocketAyncEventAgs-CSlass
元の英語の住所http://www.codeproject.com/Articles/22918/How-To-Use-the-SocketAsyncEventArgs-Class 翻訳:
説明:
私はずっと高性能のsocketクライアントを探しています.以前、伝統的なSocket類(BeginnSend、BeginnerReceiveなど)に基づく非同期方式のプログラムを書きました.しかし彼は私が要求したほど効率的な表現をしていません.そして、事件に基づく非同期的な方法を発見しました.
背景
閉塞スレッドが減少したので,非同期プログラミングモード(APM)はI/o密型アプリケーションに広く適用され効率的であった.APMは.NET Freamworkの第一版の時に実装されましたが、今まではC胪3.0にlamdaのような新しい技術が使われています.ソケットのプログラミングに特化して、新しいモデルは比較的簡単なコードのAPMを提供します.これは、オブジェクトの割り当てとゴミ収集の作業を減らすために、SocketAcEventAgsクラスを使用して文脈を維持するI/O操作です.
以下は原文です.
Introduction
I was look for a higperformance code for a client socket.Prevousy,I wrote a code based on the tradional asyncronous programming model methods from the  Socket クラス(BeginSend、  BeginReceive、and so on.But it didn't fill the performance requirements I need.The n,I found the new model for event-based asyncrous operation(see「Get Conneced with the.NET Frame 3.5」Septine.
Background
The Asynechronous Programming Model(APM)is used widely in I/O-bound aplications to achieve hieve hieve hiperformance、due to the reductctio of blocking threads.APM isimplemened in the.NET Fraameeeeeemomoork Spinininininininininininininininininininininininininininininininininininininininininininininininininininininininininininin. NET FFrererererererererererererererererererererererererefor socket programming、the new model for APM delivers an easure coding,not to mention the performance benefits.It is done towards the use of the  SocketAsyncEventArgs clast to keep the context between I/O operation s、which reduces Object allocation and the garbage collection working.
The  SocketAsyncEventArgs class is also available in the.NET Framewak 2.0 SP 1,and the code from this artic was written using Microsoft Visual Studio.NET 2008.
Using the Code
To begin with the  SocketAsyncEventArgs クラス、I studied the example from MSDN、but there was something missing:the  AsyncUserToken class.I undestood the class shound expose a property  Socket,coreponding to the socket used to perform the I/O operation.But how to keep data received from client until the accept operation ends?Since  UserToken is an  Object、it could accept anything、so I created a  Token class to traccept operation.Below shown are the modified methods to use the instance of  Token クラスas the  UserToken.

 Collappse |  Copyコード
// Process the accept for the socket listener.
private void ProcessAccept(SocketAsyncEventArgs e)
{
    Socket s = e.AcceptSocket;
    if (s.Connected)
    {
        try
        {
            SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop();
            if (readEventArgs != null)
            {
                // Get the socket for the accepted client connection and put it into the 
                // ReadEventArg object user token.
                readEventArgs.UserToken = new Token(s, this.bufferSize);

                Interlocked.Increment(ref this.numConnectedSockets);
                Console.WriteLine("Client connection accepted. 
			There are {0} clients connected to the server",
                    this.numConnectedSockets);

                if (!s.ReceiveAsync(readEventArgs))
                {
                    this.ProcessReceive(readEventArgs);
                }
            }
            else
            {
                Console.WriteLine("There are no more available sockets to allocate.");
            }
        }
        catch (SocketException ex)
        {
            Token token = e.UserToken as Token;
            Console.WriteLine("Error when processing data received from {0}:\r
{1}"
, token.Connection.RemoteEndPoint, ex.ToString()); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } // Accept the next connection request. this.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) { Token token = e.UserToken as Token; token.SetData(e); Socket s = token.Connection; if (s.Available == 0) { // Set return buffer. token.ProcessData(e); if (!s.SendAsync(e)) { // Set the buffer to send back to the client. this.ProcessSend(e); } } else if (!s.ReceiveAsync(e)) { // Read the next block of data sent by client. this.ProcessReceive(e); } } else { this.ProcessError(e); } } else { this.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. Token token = e.UserToken as Token; if (!token.Connection.ReceiveAsync(e)) { // Read the next block of data send from the client. this.ProcessReceive(e); } } else { this.ProcessError(e); } }
I also modified the code to show here you can maipulate the message received by the listener.In the example,I created the method  ProcessData  in the  Token class to echong back to the client the received message.
To control the listener lifetime,an instance of the  Mutex class is used.The  Start method、which is based on the original  Init method creates the mutex,and the cores ponding  Stop method releass the mtex.The methods are suitable to implement the socket server as a Windows Service.

 Collappse |  Copyコード
// Starts the server such that it is listening
// for incoming connection requests.
internal void Start(Int32 port)
{
    // Get host related information.
    IPAddress[] addressList = 
          Dns.GetHostEntry(Environment.MachineName).AddressList;

 Collappse |  Copyコード
    // 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.
    this.listenSocket.Listen(this.numConnections);

    // 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 using the  SocketAsyncEventArgs class.Although the MSDN says the class is specifilially designed for network server appications,there isのrestication in using this APM in a client code.Below,there is the  SocketClien class、written in this way:

 Collappse |  Copyコード
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);

            // Address 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 asynchronously.
                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 the first entry in the address from the host.Instead,you shound use the last address,as shown in the  Start method.Another technique presented here is how to set the dual mode for an IP 6 address family,which is helpful fent to run the server in a Windows Vista Windows Server 2008,which enables IP 6 by deult.
Both programs use command line argments to run.In the client example,you shoul d inform“localhost”as the name of the host instead of the machine name if both the server and the client the client manning of Windows.
ヒットマン
15 January,2008-Original version posted 28 September、2010-Updated article and server example License
This artic、along with any assicated source code and files、is licensed under The Code Project Open License(CPOL)
About the Author
Marcos Hdalgo Nunes
SocketAsyncEventArgs类的使用和详细说明_第1张图片