2013-04-14 76 views
0

我現在正在編寫一個基於SocketAsyncEventArgs的小型框架,該類是基於IOCP創建的,該類比APM模式效率更高。 但在這裏,我運行測試時遇到了一些問題。 這裏是服務器代碼:爲什麼我的TCP連接似乎在服務器端丟失數據包?

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

namespace SocketServer 
{ 
public class Server 
{ 
    Socket serverSocket; 
    SocketAsyncEventArgs socketAsyncEventArgs; 
    SocketAsyncEventArgsPool readWritePool; 
    HandleMessage handleMessage; 
    BufferManager buffeManager; 

    const int PrefixSize = 11; 

    public void Init(int port,int connections,int receiveBufferSize) 
    { 
     buffeManager = new BufferManager(receiveBufferSize * connections * 2, receiveBufferSize); 

     buffeManager.InitBuffer(); 

     readWritePool = new SocketAsyncEventArgsPool(connections); 

     SocketAsyncEventArgs socketAsyncEventArgsPooling; 
     for (int i = 0; i < connections; i++) 
     { 
      socketAsyncEventArgsPooling = new SocketAsyncEventArgs(); 
      socketAsyncEventArgsPooling.Completed += readEventArgsIO_Completed; 

      buffeManager.SetBuffer(socketAsyncEventArgsPooling); 
      readWritePool.Push(socketAsyncEventArgsPooling); 
     } 

     handleMessage = new HandleMessage(); 

     IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList; 
     IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port); 

     this.serverSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 


     if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6) 
     { 
      this.serverSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false); 
      this.serverSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port)); 
     } 
     else 
     { 
      this.serverSocket.Bind(localEndPoint); 
     } 

     this.serverSocket.Listen(100); 

     StartAccept(null); 
    } 

    private void StartAccept(SocketAsyncEventArgs acceptSocketAsyncEventArgs) 
    { 
     if (acceptSocketAsyncEventArgs == null) 
     { 
      acceptSocketAsyncEventArgs = new SocketAsyncEventArgs(); 
      acceptSocketAsyncEventArgs.Completed += socketAsyncEventArgs_Completed; 
     } 
     else 
     { 
      acceptSocketAsyncEventArgs.AcceptSocket = null; 
     } 

     Boolean willRaiseEvent = this.serverSocket.AcceptAsync(acceptSocketAsyncEventArgs); 
     if (!willRaiseEvent) 
     { 
      this.ProcessAccept(acceptSocketAsyncEventArgs); 
     } 
    } 

    private void socketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) 
    { 
     ProcessAccept(e); 
    } 

    private void readEventArgsIO_Completed(object sender, SocketAsyncEventArgs e) 
    { 
     switch (e.LastOperation) 
     { 
      case SocketAsyncOperation.Receive: 
       this.ProcessReceive(e); 
       break; 
      case SocketAsyncOperation.Send: 
       //this.ProcessSend(e); 
       break; 
      default: 
       throw new ArgumentException("The last operation completed on the socket was not a receive or send"); 
     } 
    } 


    private void ProcessAccept(SocketAsyncEventArgs e) 
    { 

     SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop(); 
     //SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs(); 
     readEventArgs.UserToken = e.AcceptSocket; 

     Console.WriteLine("---------------------------------------------------"); 
     Console.WriteLine("Client Connected {0}",e.AcceptSocket.RemoteEndPoint); 

     Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); 

     if (!willRaiseEvent) 
     { 
      this.ProcessReceive(readEventArgs); 
     } 

     this.StartAccept(e); 
    } 

    private void ProcessReceive(SocketAsyncEventArgs e) 
    { 
     if (e.BytesTransferred > 0) 
     { 
      if (e.SocketError == SocketError.Success) 
      { 
       Console.WriteLine("receiving data, {0} bytes", e.BytesTransferred); 
       Socket socket = e.UserToken as Socket; 

       int bytesTransferred = e.BytesTransferred; 

       string received = Encoding.ASCII.GetString(e.Buffer, e.Offset, bytesTransferred); 


       Console.WriteLine("Received:{0}", received); 

       string[] msgArray = handleMessage.GetActualString(received); 

       foreach (var msg in msgArray) 
       { 
        Console.WriteLine("After Split:{0}", msg); 
       } 

       // Array.Clear(e.Buffer, e.Offset, bytesTransferred); 

       Boolean willRaiseEvent = socket.SendAsync(e); 
       if (!willRaiseEvent) 
       { 
        this.ProcessSend(e); 
       } 

       readWritePool.Push(e); 
      } 
     } 

    } 

    private void ProcessSend(SocketAsyncEventArgs e) 
    { 

    } 
} 
} 

這裏是我的客戶端代碼:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Net.Sockets; 
using System.Threading; 
using System.Net; 

namespace SocketClient 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.2.129"), 1234); 

     SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs(); 

     connectArgs.RemoteEndPoint = ipEndPoint; 
     connectArgs.Completed += OnConnected; 

     socket.ConnectAsync(connectArgs); 

     socket.SendBufferSize = Int16.MaxValue; 

     //NetworkStream streamToServer = new NetworkStream(socket); 
     string text = "[length=12]Hello server"; 
     byte[] sendBuffer = Encoding.ASCII.GetBytes(text); 


     for (int i = 0; i < 5; i++) 
     { 
      SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs(); 

      sendArgs.UserToken = socket; 
      sendArgs.SetBuffer(sendBuffer,0,sendBuffer.Length); 
      sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend); 


      socket.SendAsync(sendArgs); 
     } 

     Console.ReadLine(); 
    } 

    private static void OnSend(object sender, SocketAsyncEventArgs e) 
    { 
     Console.WriteLine("SendOk: {0}", e.UserToken.ToString()); 
    } 

    private static void OnConnected(object sender, SocketAsyncEventArgs e) 
    { 
     Console.WriteLine("Conectioned"); 
    } 
} 
} 

但是當我開始幾個客戶,我發現,有時服務器可以正確地接收郵件;但有時候,服務器只能接收到第一條消息,剩下的消息看起來都是「丟失」的,任何人都可以建議?謝謝。

我從別人那裏聽說,我應該實現自己的協議來傳輸數據,但任何人都可以告訴我如何定義? THX

下面

是服務器端的屏幕拍攝的捕獲: enter image description here

+0

定義「我有一些問題」。 – EJP

回答

1

問題描述發生,因爲在服務器代碼,如果整條消息已被接收或ReceiveAsync方法被調用一次,無論不。其餘的只是沒有閱讀。

ReceiveAsync documentation on MSDN所述,「對於字節流式套接字,傳入數據將被放入緩衝區,直到緩衝區被填滿,連接關閉或內部緩衝數據已耗盡。」。在你的情況下,如果客戶端發送的消息被分成幾個塊,那麼當數據的第一部分到達服務器時,它被系統放置在套接字的內部緩衝區中。如果您有一個ReceiveAsync方法等待數據,它會讀取內部緩衝數據,直到它耗盡,然後返回,即使這只是第一個塊,並且仍然有數據要發送。您將需要另一個ReceiveAsync操作來獲取它。如果您想檢查這是否屬實,您可以嘗試將Thread.Sleep(200)放置在發送來自客戶端的5條消息的for循環中。在這種情況下,服務器僅接收消息的第一部分的機會將變得非常高,因爲TCP使用一些算法來有效地發送數據,並且這個超時將決定它分別發送5條消息。但是,您無法控制消息在客戶端和服務器之間的網絡中如何分片。即使僅使用一個SendAsync操作發送整個消息,也可能需要多個ReceiveAsync操作。

要解決讀取服務器上部分消息的問題,您將需要知道您期望的字節數。這可以通過使用一個固定的消息長度或者通過一些協議來確定長度來完成,例如用從客戶端發送的每個消息加上將要發送的消息的字節數。服務器將不得不進行幾次ReceiveAsync調用,直到收到整個長度。爲了做到這一點,服務器將需要保持剩餘的字節數來接收。 你可以找到一個完整的和解釋的SocketAsyncEventArgs client-server application on CodeProject的例子,並理解它會幫助你解決你的問題。

+0

thx。我通過增加一個receiveasync方法解決了這個問題。 – CharlieShi

相關問題