2012-10-30 58 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