none
SocketAsyncEventArgs使用问题 RRS feed

  • 问题

  • 地址:

    http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx

     

    在MSDN上面看到 如果使用SocketAsyncEventArgs可以提高SOCKET方面很多性能,所以就准备测试一下。

    但是在测试示例的过程中,产生了一点不明白的地方。

    首先就是BufferManager这个,这个类应该是为了减少内存碎片。

    但是用时候发现,如果我发送如:e.SetBuffer(temp,0, temp.Length);,那么大小就会被重写,下次接受数据也只能按上次发送的大小N次接受。

                        temp = Encoding.UTF8.GetBytes("haha");
                        e.SetBuffer(temp,0, temp.Length);
                        willRaiseEvent = token.Socket.SendAsync(e);

    那么,下次我接受时候,e.Buffer=4..每次只能接受4个字节了。

    是不是发送时候,需要设置大小?

    我想法:在开始时候byte[] temp = new byte[e.Count]; 不过,这样没解决内存问题吧?? 还是有其他方法。

     


    梦得彼岸~~
    2010年4月19日 9:58

答案

  • 你好,

    你有没研究过上面文档中的C#的一个例子,如果没有,我给你贴在这,你看看。上面的注释还比较清楚。

    根据上面的代码,应该是每次都需要设置的。其实我觉得这样已经是充分利用内存了。

    如果用200个bytes的数组去接收只有100个bytes的内容,肯定是不合理了。

    // 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);
          readWriteEventArg.UserToken = new AsyncUserToken();
    
          // 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
        StartAccept(null);      
    
        //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)
      {
        if (acceptEventArg == null)
        {
          acceptEventArg = new SocketAsyncEventArgs();
          acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
        }
        else
        {
          // socket must be cleared since the context object is being reused
          acceptEventArg.AcceptSocket = null;
        }
    
        m_maxNumberAcceptedClients.WaitOne();
        bool 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);
      }
    
      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();
        ((AsyncUserToken)readEventArgs.UserToken).Socket = 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);
        }
    
        // Accept the next connection request
        StartAccept(e);
      }
    
      // 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
        AsyncUserToken token = (AsyncUserToken)e.UserToken;
        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);
          bool willRaiseEvent = token.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
          AsyncUserToken token = (AsyncUserToken)e.UserToken;
          // read the next block of data send from the client
          bool willRaiseEvent = token.Socket.ReceiveAsync(e);
          if (!willRaiseEvent)
          {
            ProcessReceive(e);
          }
        }
        else
        {
          CloseClientSocket(e);
        }
      }
    
      private void CloseClientSocket(SocketAsyncEventArgs e)
      {
        AsyncUserToken token = e.UserToken as AsyncUserToken;
    
        // close the socket associated with the client
        try
        {
          token.Socket.Shutdown(SocketShutdown.Send);
        }
        // throws if client process has already closed
        catch (Exception) { }
        token.Socket.Close();
    
        // decrement the counter keeping track of the total number of clients connected to the server
        Interlocked.Decrement(ref m_numConnectedSockets);
        m_maxNumberAcceptedClients.Release();
        Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
    
        // Free the SocketAsyncEventArg so they can be reused by another client
        m_readWritePool.Push(e);
      }
    
    }  
    
    

     


    Microsoft Online Community Support
    • 已建议为答案 络绎 2010年4月21日 7:26
    • 已标记为答案 KeFang Chen 2010年4月23日 6:41
    2010年4月21日 2:12

全部回复

  •     internal sealed class BufferManager {


            private Byte[] buffer;

            private int bufferSize;


            private int currentIndex;


            private Stack<int> freeIndexPool;


            private int numBytes;


            internal BufferManager(int totalBytes, int bufferSize)
            {
                this.numBytes = totalBytes;
                this.currentIndex = 0;
                this.bufferSize = bufferSize;
                this.freeIndexPool = new Stack<int>();
            }


            internal void FreeBuffer(SocketAsyncEventArgs args) {
                this.freeIndexPool.Push(args.Offset);
                args.SetBuffer(null, 0, 0);
            }

            /// <summary>
            /// 分配缓冲池所使用的缓冲区空间
            /// </summary>
            internal void InitBuffer() {
                this.buffer = new Byte[this.numBytes];
            }


            internal Boolean SetBuffer(SocketAsyncEventArgs args) {
                if (this.freeIndexPool.Count > 0) {
                    args.SetBuffer(this.buffer, this.freeIndexPool.Pop(), this.bufferSize);
                }
                else {
                    if ((this.numBytes - this.bufferSize) < this.currentIndex) {
                        return false;
                    }
                    args.SetBuffer(this.buffer, this.currentIndex, this.bufferSize);

                    this.currentIndex += this.bufferSize;
                }

                return true;
            }
        }


    梦得彼岸~~
    2010年4月19日 10:01
  • 你好,

    你有没研究过上面文档中的C#的一个例子,如果没有,我给你贴在这,你看看。上面的注释还比较清楚。

    根据上面的代码,应该是每次都需要设置的。其实我觉得这样已经是充分利用内存了。

    如果用200个bytes的数组去接收只有100个bytes的内容,肯定是不合理了。

    // 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);
          readWriteEventArg.UserToken = new AsyncUserToken();
    
          // 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
        StartAccept(null);      
    
        //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)
      {
        if (acceptEventArg == null)
        {
          acceptEventArg = new SocketAsyncEventArgs();
          acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
        }
        else
        {
          // socket must be cleared since the context object is being reused
          acceptEventArg.AcceptSocket = null;
        }
    
        m_maxNumberAcceptedClients.WaitOne();
        bool 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);
      }
    
      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();
        ((AsyncUserToken)readEventArgs.UserToken).Socket = 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);
        }
    
        // Accept the next connection request
        StartAccept(e);
      }
    
      // 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
        AsyncUserToken token = (AsyncUserToken)e.UserToken;
        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);
          bool willRaiseEvent = token.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
          AsyncUserToken token = (AsyncUserToken)e.UserToken;
          // read the next block of data send from the client
          bool willRaiseEvent = token.Socket.ReceiveAsync(e);
          if (!willRaiseEvent)
          {
            ProcessReceive(e);
          }
        }
        else
        {
          CloseClientSocket(e);
        }
      }
    
      private void CloseClientSocket(SocketAsyncEventArgs e)
      {
        AsyncUserToken token = e.UserToken as AsyncUserToken;
    
        // close the socket associated with the client
        try
        {
          token.Socket.Shutdown(SocketShutdown.Send);
        }
        // throws if client process has already closed
        catch (Exception) { }
        token.Socket.Close();
    
        // decrement the counter keeping track of the total number of clients connected to the server
        Interlocked.Decrement(ref m_numConnectedSockets);
        m_maxNumberAcceptedClients.Release();
        Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
    
        // Free the SocketAsyncEventArg so they can be reused by another client
        m_readWritePool.Push(e);
      }
    
    }  
    
    

     


    Microsoft Online Community Support
    • 已建议为答案 络绎 2010年4月21日 7:26
    • 已标记为答案 KeFang Chen 2010年4月23日 6:41
    2010年4月21日 2:12