2012-05-07 82 views
-1

我一直試圖讓這個工作一段時間,我甚至搜索了大部分論壇,但到目前爲止我一無所獲。C#TCP服務器緩衝區

我的問題: Process_send完美地工作,因爲我的客戶端可以接收服務器發送的所有內容。但是,當客戶端說了些什麼時,即使TransferredBytes屬性顯示爲「46」,process_receive函數中的緩衝區也會返回爲null,這應該是。

請讓我知道如何解決此問題。謝謝!

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Net.Sockets; 
using System.Threading; 
using System.Net; 

namespace MonkeySyncServer 
{ 
    public partial class ServerForm : Form 
    { 
     public ServerForm() 
     { 
      InitializeComponent(); 
     } 

     private void btn_startServer_Click(object sender, EventArgs e) 
     { 
      Server s = new Server(100, 1024); 
      s.Init(); 
      s.Start(new IPEndPoint(IPAddress.Any, 1000)); 
     } 


    } 
    /// <summary> 
    /// Implements the connection logic for the socket server. 
    /// </summary> 
    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; 

     /// <summary> 
     /// Create an uninitialized server instance. To start the server listening for connection requests 
     /// call the Init method followed by Start method 
     /// </summary> 
     /// <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); 
     } 

     /// <summary> 
     /// Initializes the server by preallocating reusable buffers and context objects. These objects do not 
     /// need to be preallocated or reused, by is done this way to illustrate how the API can easily be used 
     /// to create reusable objects to increase server performance. 
     /// </summary> 
     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); 
      } 

     } 

     /// <summary> 
     /// Starts the server such that it is listening for incoming connection requests.  
     /// </summary> 
     /// <param name="localEndPoint">The endpoint which the server will listening for conenction 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); 


     } 


     /// <summary> 
     /// Begins an operation to accept a connection request from the client 
     /// </summary> 
     /// <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); 
      } 
     } 

     /// <summary> 
     /// This method is the callback method associated with Socket.AcceptAsync operations and is invoked 
     /// when an accept operation is complete 
     /// </summary> 
     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; 
      byte[] d = e.Buffer; 

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

      d = d; 
      if (!willRaiseEvent) 
      { 
       ProcessReceive(readEventArgs); 
      } 

      // Accept the next connection request 
      StartAccept(e); 
     } 



     /// <summary> 
     /// This method is called whenever a receive or send opreation is completed on a socket 
     /// </summary> 
     /// <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"); 
      } 

     } 

     private bool sendToClient(AsyncUserToken token, SocketAsyncEventArgs e, string s) 
     { 
      // Send 'Hello World' to the server 
      byte[] buffer = Encoding.UTF8.GetBytes(s); 
      Console.WriteLine("Sending: " + buffer.Length + " bytes of data"); 
      e.SetBuffer(buffer, 0, buffer.Length);    
      bool willRaiseEvent = token.Socket.SendAsync(e); 
      return willRaiseEvent; 
     } 

     /// <summary> 
     /// This method is invoked when an asycnhronous 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. 
     /// </summary> 
     private void ProcessReceive(SocketAsyncEventArgs e) 
     { 
      m_bufferManager.SetBuffer(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); 
       Console.WriteLine("Received from client: {0}", Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred)); 


       byte[] data = new byte[1024]; 
       data = e.Buffer; 
      String dataString = Encoding.UTF8.GetString(data); 


       if (dataString.StartsWith("hello")) 
       { 
        token.encryptionKey = Encoding.UTF8.GetString(data).Substring(5, dataString.IndexOf("world") - 5); 
        Console.WriteLine(token.encryptionKey); 
       } 
       /* bool willRaiseEvent = sendToClient(token, e, "hello world"); 

       if (!willRaiseEvent) 
       { 
        ProcessSend(e); 
       }*/ 



      } 
      else 
      { 
       CloseClientSocket(e); 
      } 



     } 

     /// <summary> 
     /// 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 
     /// </summary> 
     /// <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) 
       { 
       // e.SetBuffer(e.Offset, listenSocket.ReceiveBufferSize); 
       //  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); 
     } 

    } 
} 
+2

你已經發布了超過300行代碼是很多要求任何人翻閱。你能提煉你的代碼下降到僅造成問題,但仍編譯必不可少位? –

+1

FYI的人誰感覺就像攻擊這個問題,這是copypasta與原始源位置: http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx –

+0

感謝,我應該已經提到,這個代碼是從那裏... – rkrishnan2012

回答

1

只是基於代碼的瘋狂猜測。我建議你用一個調試器來完成這段代碼,當你這樣做的時候,答案應該很清楚。

這些線是可疑:

byte[] data = new byte[1024]; 
data = e.Buffer; // reassigns data, so that byte[1024] gets ignored. 
String dataString = Encoding.UTF8.GetString(data); // Ignores the e.Offset value. 

我的猜測是,緩衝區得到初始化爲零,但對我來說,原因不明的套接字數據不會被讀入緩衝區的開始,而是在別處。 UTF8編碼器將0作爲第一個字符(0表示UTF8中的字符串結尾符號指示符),並返回一個空字符串,該字符串與null不同。

替換爲三條線:

String dataString = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred); 
+0

謝謝!我做了類似的事情,現在可以工作。 – rkrishnan2012