2014-02-25 26 views
1

我正在將一個AS3客戶端移植到C#,並且在完成登錄過程到服務器時遇到很大問題。將AS3轉換爲C#異步套接字不接收所有數據

我沒有訪問服務器,只有協議,並且不知道服務器是否期待有關套接字的任何特定行爲。

我設法連接到服務器並完成登錄過程。然後,我可以發送一條請求數據的消息,從而將一系列消息發送給我的客戶。

在客戶端的AS3版本中,我在三個不同的緩衝區中接收消息。

在我的C#中,我只獲得前兩個緩衝區,然後短時間重新連接。

該協議是二進制的。前2個字節告訴我消息的長度,第2個2字節的消息類型。其餘是數據。

在第一次讀取時,我得到了一個91字節的策略文件,我放棄了。之後,我收到了我能夠處理的數據,並且前20條消息都很好。雖然第三個緩衝區永遠不會到達。

任何想法?是我的錯誤AsyncSocket的實現,還是有一些我應該在我的套接字上使用的標誌?

任何指針將不勝感激。

public abstract class AsyncSocket 
{ 
    public class StateObject 
    {    
     public Socket workSocket = null; 
     public const int BufferSize = 4096; 
     public byte[] buffer = new byte[BufferSize]; 
     public byte[] messageBuffer = new byte[0]; 
    } 

    public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e); 

    public delegate void ConnectedHandler(object sender, EventArgs e); 

    public event MessageReceivedHandler MessageReceived; 

    public event ConnectedHandler Connected; 

    private IPAddress[] addresses; 
    private int port; 
    private WaitHandle addressesSet; 
    private Socket socket; 
    private int failedConnectionCount; 
    private StateObject state; 

    public AsyncSocket(IPAddress address, int port) : this(new[] { address }, port) { } 

    public AsyncSocket(IPAddress[] addresses, int port) : this(port) 
    { 
     this.addresses = addresses; 
    } 

    public AsyncSocket(string hostNameOrAddress, int port) : this(port) 
    { 
     addressesSet = new AutoResetEvent(false); 
     Dns.BeginGetHostAddresses(hostNameOrAddress, GetHostAddressesCallback, null); 
    } 

    private void GetHostAddressesCallback(IAsyncResult result) 
    { 
     addresses = Dns.EndGetHostAddresses(result);   
     ((AutoResetEvent)addressesSet).Set(); 
    } 

    private AsyncSocket(int port) 
    { 
     this.port = port; 
     this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);    
     this.Encoding = Encoding.Default; 
     this.state = new StateObject(); 
     state.workSocket = socket; 
    } 

    public Encoding Encoding { get; set; } 

    public Account Account { get; set; } 

    public void Connect() 
    { 
     if (addressesSet != null) 
      addressesSet.WaitOne();    
     Interlocked.Exchange(ref failedConnectionCount, 0); 
     socket.BeginConnect(addresses, port, ConnectCallback, socket); 
    } 

    private void ConnectCallback(IAsyncResult result) 
    { 
     try 
     { 
      Socket client = (Socket)result.AsyncState; 
      client.EndConnect(result); 
      if (Connected != null) 
      { 
       Connected(this, new EventArgs()); 
      } 
      Receive(client); 
     } 
     catch 
     { 
      Interlocked.Increment(ref failedConnectionCount); 
      if (failedConnectionCount >= addresses.Length) 
      { 
       return; 
      } 
     } 
    } 

    public void Send(string data) 
    { 
     byte[] bytes = Encoding.GetBytes(data); 
     Send(bytes); 
    } 

    public void Send(MsgHead msg) 
    { 
     byte[] bytes = msg.write(); 
     Send(bytes); 
    } 

    public void Send(byte[] bytes) 
    { 
     int messageLength = BitConverter.ToUInt16(bytes, 0); 
     int messageType = BitConverter.ToUInt16(bytes, 2); 

     Console.Out.WriteLine("Sending:len:{0} msg:{1}", messageLength, messageType); 

     socket.BeginSend(bytes, 0, bytes.Length, 0, new AsyncCallback(WriteCallback), socket); 
    } 

    private void WriteCallback(IAsyncResult result) 
    { 
     Socket client = (Socket)result.AsyncState; 
     int bytesSent = client.EndSend(result); 
     Console.WriteLine("Sent {0} bytes to server.", bytesSent); 
    } 

    private void Receive(Socket client) 
    { 
     try 
     {    
      client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
       new AsyncCallback(ReceiveCallback), state); 
     } 
     catch (Exception e) 
     { 
      Account.Window.Exit(string.Format("Error on receive: {0}",e.Message)); 
     } 
    } 

    private void ReceiveCallback(IAsyncResult result) 
    { 
     StateObject state = (StateObject)result.AsyncState; 
     Socket client = state.workSocket; 

     SocketError errorCode; 
     int bytesRead = client.EndReceive(result, out errorCode); 
     if (errorCode != SocketError.Success) 
     { 
      Account.Window.Exit(string.Format("Disconnected, {0}", errorCode.ToString())); 
      return; 
     }    

     if (bytesRead == 0) 
     { 
      Account.Window.Exit("Disconnected, zero bytes"); 
      return; 
     } 

     state.messageBuffer = state.messageBuffer.Concat(state.buffer.Take(bytesRead).ToArray()).ToArray(); 

     int messageLength = BitConverter.ToUInt16(state.messageBuffer, 0); 

     if (messageLength > 4096) 
     { 
      state.messageBuffer = state.messageBuffer.Skip(91).ToArray(); 
      messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0); 
     } 

     while (messageLength > 0 && state.messageBuffer.Length >= messageLength) 
     { 
      int messageType = BitConverter.ToUInt16(state.messageBuffer, 2); 

      Console.Out.WriteLine("Received:len:{0} msg:{1}", messageLength, messageType); 

      if (MessageReceived != null) 
      { 
       MessageReceived(this, new MessageReceivedEventArgs(state.messageBuffer.Take(messageLength).ToArray())); 
      } 

      state.messageBuffer = state.messageBuffer.Skip(messageLength).ToArray(); 

      messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0); 
     } 

     Receive(client); 
    } 
} 

回答

0

有時候TCP幀會有些棘手。你永遠不知道你可能會得到多少次接收電話,這些電話是你期待的三種緩衝器。這裏有幾件事要檢查:

ReceiveCallback()方法中可能出現的一個問題,如果您只接收1個字節,您嘗試將messageLength解碼爲2個字節的Int16將失敗。

確保MessageBuffer方法都正常工作。

MessageLength的值是否包含在MessageLength的值中?如果沒有,請確保在解碼消息字節之前跳過這2個字節。

更安全的是丟棄全部的消息字節,以防萬一它不總是91字節長。

我會將所有「完整」消息緩衝區分派到另一個方法中,並放棄或處理那裏,以保持ReceiveCallback()方法的簡潔。

+0

感謝您的回覆。我已經添加了一個檢查,以確保在計算消息長度之前至少有2個字節。我也曾經使用BackgroundWorker來處理「完整」消息的ConcurrentQueue,但這沒有什麼區別。 讓我困惑的是,我只是不接受第三個緩衝區,我做錯了什麼?或者更可能是服務器期望的行爲? –

+0

聽起來像你的數據請求正常工作。我會嘗試計算在兩個客戶端的初始數據請求後收到的總字節數。如果匹配,至少你知道問題出在C#客戶端的TCP消息框架中。 (我懷疑檢查> 4096和跳過91個字節,這太硬編碼了)。 –