2012-10-30 66 views
2

我想使用StreamReader和StreamWriter通過TCPClient.NetworkStream接收和發送數據。代碼描述如下:
客戶端代碼:
TCPClient未接收數據

using (TcpClient client = new TcpClient()) 
      { 
       IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001); 
       client.Connect(localEndPoint); 
       client.NoDelay = true; 

       using(NetworkStream stream = client.GetStream()) 
       {      
        using (StreamWriter writer = new StreamWriter(stream)) 
        { 
         writer.AutoFlush = true; 
         using (StreamReader reader = new StreamReader(stream)) 
         {  

          string message = "Client says: Hello"; 
          writer.WriteLine(message); 

       // If I comment the line below, the server receives the first message 
       // otherwise it keeps waiting for data 
          string response = reader.ReadToEnd(); 
          Console.WriteLine(response); 
         } 
         ; 
        } 
       } 
      } 


注意:如果我評論的reader.ReadToEnd();那麼服務器會收到消息,否則服務器會一直等待來自客戶端的數據。是ReadToEnd的問題?

服務器代碼接受連接異步,但以同步的方式處理數據:

class Program 
    { 
     private static AsyncCallback tcpClientCallback; 
     static void Main(string[] args){ 

      IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"),9001); 
      TcpListener listener = new TcpListener(localEndPoint); 

      listener.Start(); 

      tcpClientCallback = new AsyncCallback(ConnectCallback); 
      AcceptConnectionsAysnc(listener); 

      Console.WriteLine("Started Aysnc TCP listener, press any key to exit (server is free to do other tasks)"); 
      Console.ReadLine(); 
     } 

     private static void AcceptConnectionsAysnc(TcpListener listener) 
     { 
      listener.BeginAcceptTcpClient(tcpClientCallback, listener); 
     } 

     static void ConnectCallback(IAsyncResult result) 
     { 
      Console.WriteLine("Received a conenct request"); 
      TcpListener listener = (TcpListener)result.AsyncState; 

      // We are starting a thread which waits to receive the data 
      // This is not exactly scalable 
      Task recieveTask = new Task(() => { HandleRecieve(result, listener); }); 
      recieveTask.Start(); 

      AcceptConnectionsAysnc(listener); 
     } 


     // This makes a blocking call - that means until the client is ready to 
     // send some data the server waits 
     private static void HandleRecieve(IAsyncResult result, TcpListener listener) 
     { 
      Console.WriteLine("Waiting for data"); 

      using (TcpClient client = listener.EndAcceptTcpClient(result)) 
      { 
       client.NoDelay = true; 

       using (NetworkStream stream = client.GetStream()) 
       { 
        using (StreamReader reader = new StreamReader(stream)) 
        { 
         using (StreamWriter writer = new StreamWriter(stream)) 
         { 
          writer.AutoFlush = true; 
          Stopwatch watch = new Stopwatch(); 
          watch.Start(); 
          string data = reader.ReadToEnd(); 
          watch.Stop(); 
          Console.WriteLine("Time: " + watch.Elapsed.TotalSeconds); 
          Console.WriteLine(data); 


// Send a response to client 
           writer.WriteLine("Response: " + data); 
          } 

         } 
        }    


       } 

      } 

     } 
+0

也許有什麼讀寫流的速度.. –

回答

6

甲流沒有結束,直到它關閉。目前,客戶端和服務器端都保持連接開放,並且兩者都試圖從另一端讀取到末端--並且當他們這樣做時,將自行關閉。這是一個僵局:既不會結束,也不會結束(這可以讓另一個結束)。

選項:

  • 使用原始套接字,並讓客戶端發送後關閉出境流:

    socket.Shutdown(SocketShutdown.Send); 
    

    這讓服務器讀取到完成,這讓服務器關閉,讓客戶讀取完成;在關閉出站套接字後,客戶端仍可以從入站流中讀取數據。

  • 使用與ReadToEnd不同的終止隱喻;對於基於文本的協議,行尾是最常見的(所以:ReadLine);對於二進制協議,消息的長度前綴是最常見的。這允許每個末端讀取下一個消息,而不必讀取到流的末尾。在這種情況下,它將只有單個消息,但該策略仍然有效。

  • 使用不同的協議,如果你發現上述棘手; http適用於單個請求/響應操作,可以通過HttpListener在服務器上處理。

此外,我會很小心的服務器(甚至ReadLine)使用ReadToEnd - 這可能是在服務器上鎖定了線程的一個好辦法。如果服務器需要應對高吞吐量和大量連接,則基於原始套接字的異步IO將是首選。

+0

是ReadToEnd是問題,用ReadLine幫助取代它。 – Ngm

+0

@Ngm個人我仍然會對在服務器上使用'ReadLine'有所保留 - 請參閱我的最後一段。我有一些TCP服務器可以完成這種類型的事情,甚至可以查找和結束行使用async-IO,只是掃描字節13/10。 –

+0

感謝Marc,那麼如果我不想使用Raw套接字並使用流,那麼最佳選擇是什麼? – Ngm