2013-11-21 31 views
1

忍讓我,這是一個複雜的問題,它是一段很長的代碼,但我已經拉了我的頭髮大約兩個星期試圖讓這個工作,挫折水平非常高。任何幫助表示讚賞!C#異步套接字服務器沒有收到來自Java客戶端的響應

我有一個基於套接字的Java服務器&客戶端使用基於XML的數據庫。我正在用一個與MySQL數據庫交談的C#替換服務器。客戶將保持原樣,直到它也可以被替換,最早可能在明年晚些時候,所以認爲它是半透明盒子(最好),而且是不變的。

我需要能夠在新服務器上一次支持大約十幾個Java客戶端,如果我們擴展我們的設施,可能需要幾十個。

獲得客戶端響應服務器需要相當長的一段時間,但硬編碼服務器確實與客戶端進行通信,儘管以非常暴力的方式。這是我對概念的證明,而且我認爲將代碼轉換爲異步服務器會比較簡單,因爲我不想使用它。那是大約兩週前。我們在上週五舉行了代碼審查,以幫助我將硬編碼的內容加入到異步服務器中。我已經取得了一些成功,客戶端響應它與用戶名的初始聯繫,但是任何進一步的通信都停止了(它應該發送基於XML的查詢,並且服務器用基於XML的響應來響應)。

程序流程如下:

啓動服務器;

啓動客戶端;

服務器發送: 「Login:」,但是,這可能是從字面上任何

客戶端發送: 「USERNAME

服務器發送: 「ACCEPTED

服務器發送: 「ACCEPTED」(不知道爲什麼這是必要的,但客戶端不響應第一個服務器發送,我不能改變它)。

客戶端發送:<PCBDataBaseCMD><Search><PCBID>33844</PCBID></Search></PCBDataBaseCMD>

服務器發送:

<Executing/> 
<PCBDatabaseReply> 
    <SearchResult> 
     <SBE_PCB_Data PCBID='33844'> 
      <Creation ActionID='e2a7' User='DELLIOTTG:192.168.1.214' Date='2013-01-23T13:16:51' PCBID='33844'> 
       <PCBDrawing>10376A</PCBDrawing> 
       <AssemblyDrawing>41528F</AssemblyDrawing> 
       <Vendor>PCA</Vendor> 
       <PONumber>99999</PONumber> 
      </Creation> 
      <Assignment ActionID='e2c1' User='DELLIOTTG:192.168.1.228' Date='2013-01-23T15:30:00' PCBID='33844'> 
       <SBESerialNumber>04104743</SBESerialNumber> 
      </Assignment> 
     </SBE_PCB_Data> 
    </SearchResult> 
</PCBDatabaseReply> 

此XML將在客戶端顯示爲預期。

重置&等待下一個客戶端請求。

這裏是硬編碼服務器(這將編譯原樣):

using System; 
using System.IO; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 

class MyTcpListener 
{ 
    public static void Main() 
    { 
     Int32 port = 8955; 
     IPAddress localAddr = IPAddress.Parse("192.168.1.137"); 
     TcpListener server = null; 
     server = new TcpListener(localAddr, port); 
     server.Start(); 
     Socket socketForClient = server.AcceptSocket(); 
     NetworkStream stream = new NetworkStream(socketForClient); 
     System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(stream); 
     System.IO.StreamReader streamReader = new System.IO.StreamReader(stream); 
     string response1 = @"<Executing/> 
      <PCBDatabaseReply> 
       <SearchResult> 
        <SBE_PCB_Data PCBID='33844'> 
         <Creation ActionID='e2a7' User='DELLIOTTG:192.168.1.214' Date='2013-01-23T13:16:51' PCBID='33844'> 
          <PCBDrawing>10376A</PCBDrawing> 
          <AssemblyDrawing>41528F</AssemblyDrawing> 
          <Vendor>PCA</Vendor> 
          <PONumber>99999</PONumber> 
         </Creation> 
         <Assignment ActionID='e2c1' User='DELLIOTTG:192.168.1.228' Date='2013-01-23T15:30:00' PCBID='33844'> 
          <SBESerialNumber>04104743</SBESerialNumber> 
         </Assignment> 
        </SBE_PCB_Data> 
       </SearchResult> 
      </PCBDatabaseReply>"; 
     try 
     { 
      while(true) 
      { 
       Console.WriteLine("Waiting for Client connection... "); 
       streamWriter.WriteLine("poke"); //this can literally be anything, just something to let the client know the server's there 
       streamWriter.Flush(); 
       string userName = streamReader.ReadLine(); 
       Console.WriteLine(userName); 
       streamWriter.WriteLine("ACCEPTED"); 
       streamWriter.Flush(); 
       streamWriter.WriteLine("ACCEPTED"); 
       streamWriter.Flush(); 
       string buffer = string.Empty; 
       bool sendFlag = true; 
       ASCIIEncoding encoder = new ASCIIEncoding(); 
       char[] c = new char[512]; 
       while (sendFlag) 
       {    
        streamReader.Read(c, 0, c.Length); 
        buffer += string.Join("", c); 
        streamReader.Read(c, 0, c.Length); 
        buffer += string.Join("", c); 
        Console.WriteLine(buffer); 
        if(streamReader.Peek() < 0) 
        { 
         sendFlag = false; 
        } 
        else 
        { 
         Console.WriteLine("Apparently not at the end?"); 
        } 
       }      
       Console.WriteLine("RECEIVED: " + buffer); 
       //} 
       Console.WriteLine("SEND: " + response1); 
       streamWriter.Write(response1); 
       streamWriter.Flush(); 
      } 
     } 
     catch(ObjectDisposedException ex) 
     { 
      Console.WriteLine("ObjectDisposedException: {0}", ex); 
     } 
     catch(SocketException e) 
     { 
      Console.WriteLine("SocketException: {0}", e); 
     } 
     finally 
     { 
      server.Stop(); 
      streamReader.Dispose(); 
      streamReader.Close(); 
     } 
     Console.WriteLine("\nHit enter to continue..."); 
     Console.Read(); 
    } 
} 

我已經轉碼成異步服務器,但我不得不承認比如何完美的默契少所有的部分一起工作,以及狀態對象如何工作來引導流量。我認爲通信可能在SendCallBack()模塊中發生故障,因爲我沒有發送「停止接收」消息,或者可能是「繼續傳輸」消息,我不確定。

這裏是異步服務器(這個不會編譯,它缺少很多MySql,命令行處理等代碼,以縮短它的好處)。

public static IPAddress IpAddress { get; set; } 
    private static readonly ManualResetEvent AllDone = new ManualResetEvent(false); 

    public static IPEndPoint GetIpEndPoint() 
    { 
     IpAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(addr => addr.AddressFamily.ToString() == "InterNetwork"); 
     if (IpAddress != null) 
     { 
      IPEndPoint localEndPoint = new IPEndPoint(IpAddress, CommandLineOptions.Port); 
      return localEndPoint; 
     } 
     return null; 
    } 
    public class StateObject 
    { 
     public Socket workSocket = null; 
     public const int bufferSize = 1024; 
     public byte[] buffer = new byte[bufferSize]; 
     public string UserName { get; set; } 
     public string XmlContent { get; set; } 
     public StringBuilder sb = new StringBuilder(); 
     public StreamReader sr; 
     public StreamWriter sw; 
    } 
    public static void StartListening(IPEndPoint localEndPoint) 
    { 
     byte[] bytes = new Byte[1024]; 
     Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     try 
     { 
      listener.Bind(localEndPoint); 
      listener.Listen(100); 
      while (true) 
      { 
       AllDone.Reset(); 
       Console.WriteLine("PCBDatabaseServer online awaiting a connection..."); 
       listener.BeginAccept(AcceptCallback, listener); 
       AllDone.WaitOne(); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.Error(e.ToString()); 
     } 
     Console.WriteLine("\nPress ENTER to continue..."); 
     Console.Read(); 
    } 
    public static void AcceptCallback(IAsyncResult ar) 
    { 
     try 
     { 
      AllDone.Set(); 
      Socket listener = (Socket) ar.AsyncState; 
      Socket handler = listener.EndAccept(ar); 
      NetworkStream nwsHandle = new NetworkStream(handler); 
      StateObject acbState = new StateObject 
       { 
        workSocket = handler, 
        sw = new StreamWriter(nwsHandle) {AutoFlush = true}, 
        sr = new StreamReader(nwsHandle) 
       }; 
      acbState.sw.WriteLine("poke"); 
      handler.BeginReceive(acbState.buffer, 0, StateObject.bufferSize, 0, ReadCallback, acbState); 
      acbState.UserName = Encoding.UTF8.GetString(acbState.buffer); 
      Console.WriteLine(acbState.UserName); 
     } 
     catch (IOException ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 
    public static void ReadCallback(IAsyncResult ar) 
    { 
     try 
     { 
      StateObject rcbState = (StateObject) ar.AsyncState; 
      rcbState.sw.Write("ACCEPTED");//**this double call is required, the client has two ReadLine() statements, pretty sure the first write could be anything, and it only sees the second one, but I can't change the client** 
      rcbState.sw.Write("ACCEPTED"); 
      string testMeToo = string.Empty;//this is where I'm trying to capture the XML data from the client, which should end up in rcbState.XmlContent, but I'm flailing here trying to get this to work. 
      bool sendFlag = true; 
      char[] c = new char[512]; 
      while (sendFlag) 
      { 
       rcbState.sr.Read(c, 0, c.Length); 
       testMeToo = string.Join("", c); 
       rcbState.sr.Read(c, 0, c.Length); 
       testMeToo += string.Join("", c); 
       Console.WriteLine(testMeToo); 
       if (rcbState.sr.Peek() < 0) 
       { 
        sendFlag = false; 
       } 
       else 
       { 
        Console.WriteLine("Apparently not at the end?"); 
       } 
      } 
      Console.WriteLine("RECEIVED: " + testMeToo); 
      rcbState.workSocket.BeginReceive(rcbState.buffer, 0, StateObject.bufferSize, 0, AcceptCallback, rcbState); 
      rcbState.XmlContent = Encoding.UTF8.GetString(rcbState.buffer); 
      Console.WriteLine("XML: " + rcbState.XmlContent); 
      rcbState.XmlContent += Encoding.UTF8.GetString(rcbState.buffer); 
      Console.WriteLine("XML: " + rcbState.XmlContent); 
      int bytesRead = rcbState.workSocket.EndReceive(ar); 
      string content = string.Empty; 
      //**things break down here, you can ignore this if statement** 
      if (bytesRead > 0) 
      { 
       while (bytesRead > 0) 
       { 
        rcbState.sb.Append(Encoding.ASCII.GetString(rcbState.buffer, 0, bytesRead)); 
        content = rcbState.sb.ToString(); 
       } 
       if (true) 
       { 
        Console.WriteLine("Read {0} bytes from socket. \nData : {1}", content.Length, content); 
        Console.WriteLine(content); 
        Send(rcbState.workSocket, content); 
       } 
       else 
       { 
        rcbState.workSocket.BeginReceive(rcbState.buffer, 0, StateObject.bufferSize, 0, ReadCallback, rcbState); 
       } 
      } 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 
    private static void Send(Socket handler, String data) 
    { 
     byte[] byteData = Encoding.ASCII.GetBytes(data); 
     NetworkStream nwsHandle = new NetworkStream(handler); 
     StateObject sendState = new StateObject 
      { 
       workSocket = handler, 
       sw = new StreamWriter(nwsHandle) {AutoFlush = true} 
      }; 
     sendState.sw.Write(data); 
    } 
    //here is where I think the communication is breaking down, I think the client may be waiting for some signal to tell it to send the next bit, but I can't figure out what that may be. 
    private static void SendCallback(IAsyncResult ar) 
    { 
     try 
     { 
      Socket listener = (Socket)ar.AsyncState; 
      Socket handler = listener.EndAccept(ar); 
      NetworkStream nwsListen = new NetworkStream(listener); 
      NetworkStream nwsHandle = new NetworkStream(handler); 
      StateObject scbState = new StateObject 
      { 
       workSocket = handler, 
       sw = new StreamWriter(nwsHandle) { AutoFlush = true }, 
       sr = new StreamReader(nwsListen) 
      }; 
      int bytesSent = scbState.workSocket.EndSend(ar); 
      Console.WriteLine("Sent {0} bytes to client.", bytesSent); 
      handler.Shutdown(SocketShutdown.Both); 
      handler.Close(); 
     } 
     catch (Exception e) 
     { 
      Log.Error(e.ToString()); 
     } 
    } 
    #endregion 
} 

}

這裏有一些鏈接我的研究,首先是一個相關的問題,而我回信說:
Source-less black box client

C#<> Java socket communications

How to write a scalable TCP/IP based server

C# High Performance Socket Code

回答

0

夫婦的事情要考慮:

  1. 你AllDone.Wait()是怎麼回事,而你繼續與客戶交談,以阻止你接受線程。這種方式違背了異步I/O的目的,因爲您希望服務器在接受連接時立即返回監聽連接,否則您的處理將被序列化。

  2. BeginReceive(...)將立即返回,並且您可能在讀取操作未完成時將數據從讀取緩衝區分配給變量。

  3. 在您的接收回調中,看起來您正在發出更多的異步讀取(BeginReceive)。不確定這是你的意圖,因爲你已經在與服務器的主接受線程分離的線程中運行。您可能需要考慮使用rcbState.workSocket.Receive(...)而不是BeginReceive(),該方法完成回到您的接受回調,該回調應該用於接受未完成讀取的連接。因此,看起來邏輯是錯誤的。

所以我的整體建議是,一旦你進入你的接受回調使用同步讀取和對新的socket你寫,因爲它似乎從我可以告訴你有一系列的客戶機/服務器交互完成「交易」。一旦接受來自客戶端的連接,似乎沒有任何價值做額外的異步讀/寫操作。你當時只是在做異步讀/寫操作而讓自己感到困惑。

+0

感謝您的建議,我會第一個承認我對此感到困惑!今天我會更多地討論這個問題,看看我能否利用你的意見。我認爲您可能會遇到有關異步和同步讀取和寫入事務的問題。我當然希望如此,我已經準備好做好這件事了。 – delliottg

相關問題