SocketAsyncEventArgs 類別

定義

表示異步套接字作業。

public ref class SocketAsyncEventArgs : EventArgs, IDisposable
public class SocketAsyncEventArgs : EventArgs, IDisposable
type SocketAsyncEventArgs = class
    inherit EventArgs
    interface IDisposable
Public Class SocketAsyncEventArgs
Inherits EventArgs
Implements IDisposable
繼承
SocketAsyncEventArgs
實作

範例

以下程式碼範例實作了使用該 SocketAsyncEventArgs 類別的套接字伺服器的連線邏輯。 接受連線後,所有從用戶端讀取的資料都會回傳給用戶端。 讀取並回傳至用戶端模式的過程會持續進行,直到用戶端斷線為止。 本範例所使用的 BufferManager 類別已顯示在本方法的 SetBuffer(Byte[], Int32, Int32) 程式碼範例中。 本範例中使用的 SocketAsyncEventArgsPool 類別已顯示在建構子的程式碼範例 SocketAsyncEventArgs 中。

// Implements the connection logic for the socket server.
// After accepting a connection, all data read from the client
// is sent back to the client. The read and echo back to the client pattern
// is continued until the client disconnects.
class Server
{
    private int m_numConnections;   // the maximum number of connections the sample is designed to handle simultaneously
    private int m_receiveBufferSize;// buffer size to use for each socket I/O operation
    BufferManager m_bufferManager;  // represents a large reusable set of buffers for all socket operations
    const int opsToPreAlloc = 2;    // read, write (don't alloc buffer space for accepts)
    Socket listenSocket;            // the socket used to listen for incoming connection requests
    // pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations
    SocketAsyncEventArgsPool m_readWritePool;
    int m_totalBytesRead;           // counter of the total # bytes received by the server
    int m_numConnectedSockets;      // the total number of clients connected to the server
    Semaphore m_maxNumberAcceptedClients;

    // Create an uninitialized server instance.
    // To start the server listening for connection requests
    // call the Init method followed by Start method
    //
    // <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param>
    // <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param>
    public Server(int numConnections, int receiveBufferSize)
    {
        m_totalBytesRead = 0;
        m_numConnectedSockets = 0;
        m_numConnections = numConnections;
        m_receiveBufferSize = receiveBufferSize;
        // allocate buffers such that the maximum number of sockets can have one outstanding read and
        //write posted to the socket simultaneously
        m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc,
            receiveBufferSize);

        m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
        m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
    }

    // Initializes the server by preallocating reusable buffers and
    // context objects.  These objects do not need to be preallocated
    // or reused, but it is done this way to illustrate how the API can
    // easily be used to create reusable objects to increase server performance.
    //
    public void Init()
    {
        // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds
        // against memory fragmentation
        m_bufferManager.InitBuffer();

        // preallocate pool of SocketAsyncEventArgs objects
        SocketAsyncEventArgs readWriteEventArg;

        for (int i = 0; i < m_numConnections; i++)
        {
            //Pre-allocate a set of reusable SocketAsyncEventArgs
            readWriteEventArg = new SocketAsyncEventArgs();
            readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);

            // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
            m_bufferManager.SetBuffer(readWriteEventArg);

            // add SocketAsyncEventArg to the pool
            m_readWritePool.Push(readWriteEventArg);
        }
    }

    // Starts the server such that it is listening for
    // incoming connection requests.
    //
    // <param name="localEndPoint">The endpoint which the server will listening
    // for connection requests on</param>
    public void Start(IPEndPoint localEndPoint)
    {
        // create the socket which listens for incoming connections
        listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        listenSocket.Bind(localEndPoint);
        // start the server with a listen backlog of 100 connections
        listenSocket.Listen(100);

        // post accepts on the listening socket
        SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
        acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
        StartAccept(acceptEventArg);

        //Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount);
        Console.WriteLine("Press any key to terminate the server process....");
        Console.ReadKey();
    }

    // Begins an operation to accept a connection request from the client
    //
    // <param name="acceptEventArg">The context object to use when issuing
    // the accept operation on the server's listening socket</param>
    public void StartAccept(SocketAsyncEventArgs acceptEventArg)
    {
        // loop while the method completes synchronously
        bool willRaiseEvent = false;
        while (!willRaiseEvent)
        {
            m_maxNumberAcceptedClients.WaitOne();

            // socket must be cleared since the context object is being reused
            acceptEventArg.AcceptSocket = null;            
            willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
            if (!willRaiseEvent)
            {
                ProcessAccept(acceptEventArg);
            }
        }
    }

    // This method is the callback method associated with Socket.AcceptAsync
    // operations and is invoked when an accept operation is complete
    //
    void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
    {
        ProcessAccept(e);
        
        // Accept the next connection request
        StartAccept(e);
    }

    private void ProcessAccept(SocketAsyncEventArgs e)
    {
        Interlocked.Increment(ref m_numConnectedSockets);
        Console.WriteLine("Client connection accepted. There are {0} clients connected to the server",
            m_numConnectedSockets);

        // Get the socket for the accepted client connection and put it into the
        //ReadEventArg object user token
        SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop();
        readEventArgs.UserToken = e.AcceptSocket;

        // As soon as the client is connected, post a receive to the connection
        bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
        if (!willRaiseEvent)
        {
            ProcessReceive(readEventArgs);
        }
    }

    // This method is called whenever a receive or send operation is completed on a socket
    //
    // <param name="e">SocketAsyncEventArg associated with the completed receive operation</param>
    void IO_Completed(object sender, SocketAsyncEventArgs e)
    {
        // determine which type of operation just completed and call the associated handler
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Receive:
                ProcessReceive(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSend(e);
                break;
            default:
                throw new ArgumentException("The last operation completed on the socket was not a receive or send");
        }
    }

    // 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 && e.SocketError == SocketError.Success)
        {
            //increment the count of the total bytes receive by the server
            Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
            Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead);

            //echo the data received back to the client
            e.SetBuffer(e.Offset, e.BytesTransferred);
            Socket socket = (Socket)e.UserToken;
            bool willRaiseEvent = socket.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
    //
    // <param name="e"></param>
    private void ProcessSend(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            // done echoing data back to the client
            Socket socket = (Socket)e.UserToken;
            // read the next block of data send from the client
            bool willRaiseEvent = socket.ReceiveAsync(e);
            if (!willRaiseEvent)
            {
                ProcessReceive(e);
            }
        }
        else
        {
            CloseClientSocket(e);
        }
    }

    private void CloseClientSocket(SocketAsyncEventArgs e)
    {
        Socket socket = (Socket)e.UserToken;

        // close the socket associated with the client
        try
        {
            socket.Shutdown(SocketShutdown.Send);
        }
        // throws if client process has already closed
        catch (Exception) { }
        socket.Close();

        // decrement the counter keeping track of the total number of clients connected to the server
        Interlocked.Decrement(ref m_numConnectedSockets);

        // Free the SocketAsyncEventArg so they can be reused by another client
        m_readWritePool.Push(e);

        m_maxNumberAcceptedClients.Release();
        Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
    }
}

備註

SocketAsyncEventArgs 類別是該類別的一系列增強 System.Net.Sockets.Socket 功能之一,提供一種替代的非同步模式,供專用的高效能套接字應用程式使用。 此課程專為需要高效能的網路伺服器應用設計。 應用程式可以專門或僅在目標熱區域使用增強型非同步模式(例如接收大量資料時)。

這些改進的主要特點是避免了在高容量非同步套接字輸入輸出期間重複分配與同步物件。 目前該 System.Net.Sockets.Socket 類別實作的開始/結束設計模式要求每個非同步套接字操作都分配一個 System.IAsyncResult 物件。

在新的 System.Net.Sockets.Socket 類別增強中,非同步套接字操作由應用程式分配並維護的可重用 SocketAsyncEventArgs 物件描述。 高效能套筒應用最清楚必須維持的重疊套筒操作數量。 應用程式可以建立所需的任意數量 SocketAsyncEventArgs 的物件。 例如,如果伺服器應用程式需要隨時有 15 個 socket 接受操作未完成,以支援進來的客戶端連線速率,它可以為此目的分配 15 個可 SocketAsyncEventArgs 重複使用的物件。

使用此類別執行非同步套接字操作的模式包含以下步驟:

  1. 分配一個新的 SocketAsyncEventArgs 上下文物件,或從應用程式池中取得一個免費的。

  2. 將上下文物件的屬性設為即將執行的操作(例如完成回調方法、資料緩衝區、緩衝區的偏移量,以及最大傳輸資料量)。

  3. 呼叫適當的套接字方法(xxxAsync)來啟動非同步操作。

  4. 如果非同步套接字方法(xxxAsync)回傳為真,則在回調時查詢上下文屬性的完成狀態。

  5. 若非同步套接字方法(xxxAsync)回傳 false,則該操作同步完成。 操作結果可查詢上下文屬性。

  6. 可以重複使用上下文用於其他操作、放回池中,或丟棄它。

新的非同步套接字操作上下文物件的壽命由應用程式碼的參考及非同步 I/O 參考決定。 應用程式在將非同步套接字操作上下文物件提交為非同步套接字操作方法的參數後,並不一定保留對該物件的參考。 它會一直被參考,直到完成回調回傳。 然而,對應用程式來說保留上下文參考是有利的,以便未來非同步套接字操作時能重複使用。

建構函式

名稱 Description
SocketAsyncEventArgs()

會產生一個空 SocketAsyncEventArgs 實例。

SocketAsyncEventArgs(Boolean)

初始化 SocketAsyncEventArgs

屬性

名稱 Description
AcceptSocket

取得或設定使用非同步套接字方法的套接字,或為接受連線所建立的套接字。

Buffer

用非同步套接字方法取得資料緩衝區。

BufferList

透過非同步套接字方法取得或設定一組資料緩衝區。

BytesTransferred

取得套接字操作中傳輸的位元組數。

ConnectByNameError

當使用 a DnsEndPoint 時,當連線失敗時,會取得例外。

ConnectSocket

在成功完成Socket方法後所建立並連接ConnectAsync的物件。

Count

在非同步操作中,取得最多資料量(以位元組計)以發送或接收。

DisconnectReuseSocket

取得或設定一個值,指定斷開操作後 socket 是否能重複使用。

LastOperation

取得最近使用此上下文物件執行的套接字操作類型。

MemoryBuffer

透過非同步套接字方法取得用作緩衝區的記憶體區域。

Offset

將位元組的偏移量輸入屬性所參考 Buffer 的資料緩衝區。

ReceiveMessageFromPacketInfo

取得收到封包的 IP 位址與介面。

RemoteEndPoint

取得或設定遠端 IP 端點進行非同步操作。

SendPacketsElements

取得或設定一個緩衝區陣列,用於方法所使用的 SendPacketsAsync(SocketAsyncEventArgs) 非同步操作。

SendPacketsFlags

取得或設定方法所使用的TransmitFileOptions非同步運算的位元組合SendPacketsAsync(SocketAsyncEventArgs)值。

SendPacketsSendSize

取得或設定用於傳送操作的資料區塊大小(以位元組為單位)。

SocketClientAccessPolicyProtocol
已淘汰.

取得或設定協定來下載套接字客戶端存取政策檔。

SocketError

取得或設定非同步套接字操作的結果。

SocketFlags

取得非同步套接字操作的結果,或設定非同步操作的行為。

UserToken

取得或設定與此非同步套接字操作相關的使用者或應用程式物件。

方法

名稱 Description
Dispose()

釋放實例所使用的 SocketAsyncEventArgs 未管理資源,並可選擇性地處理這些受管理資源。

Equals(Object)

判斷指定的物件是否等於目前的物件。

(繼承來源 Object)
Finalize()

釋放職業使用的 SocketAsyncEventArgs 資源。

GetHashCode()

做為預設哈希函式。

(繼承來源 Object)
GetType()

取得目前實例的 Type

(繼承來源 Object)
MemberwiseClone()

建立目前 Object的淺層複本。

(繼承來源 Object)
OnCompleted(SocketAsyncEventArgs)

代表非同步操作完成時所呼叫的方法。

SetBuffer(Byte[], Int32, Int32)

設定資料緩衝區使用非同步套接字方法。

SetBuffer(Int32, Int32)

設定資料緩衝區使用非同步套接字方法。

SetBuffer(Memory<Byte>)

透過非同步套接字方法設定記憶體區域作為緩衝區。

ToString()

傳回表示目前 物件的字串。

(繼承來源 Object)

事件

名稱 Description
Completed

該事件用於完成非同步操作。

適用於

另請參閱