2017-05-04 39 views
1

我正在嘗試向服務器發送命令,例如請求服務器發回其中的文件列表目錄。問題是,當我將「list」命令發送到服務器時,我必須發送它兩次,以便服務器將文件列表發送回客戶端。我確信服務器在兩個時間都接收到命令,就像在服務器端打印應該在控制檯上發送給客戶端的結果一樣,這兩次都會顯示。當我從客戶端向服務器發送命令時,客戶端僅在請求發送兩次時收到響應

我正在使用C#和TCPListeners偵聽傳入的響應或命令,並且TCPClient在服務器和客戶端之間發送響應或命令。

客戶端代碼

private TcpListener tcpListener = new TcpListener(9090); 
    private void button3_Click(object sender, EventArgs e) 
    { 
     Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes("list"); 
     try 
     { 
      TcpClient clientSocket = new TcpClient(serverIPFinal, 8080); 
      if (clientSocket.Connected) 
      { 
       NetworkStream networkStream = clientSocket.GetStream(); 
       networkStream.Write(bytesToSend, 0, bytesToSend.Length); 
       // networkStream.Close(); 
       // clientSocket.Close(); 
       thdListener = new Thread(new ThreadStart(listenerThreadList)); 
       thdListener.Start(); 
      } 
     } 
     catch 
     { 
      isConnectedLbl.Text = "Server not running"; 
     } 
    } 
    //Listener Thread to receive list of files. 
    public void listenerThreadList() 
    { 

     tcpListener.Start(); 

     while (true) 
     { 
      handlerSocket = tcpListener.AcceptSocket(); 
      if (handlerSocket.Connected) 
      { 
       Control.CheckForIllegalCrossThreadCalls = false; 
       lock (this) 
       { 
        if (handlerSocket != null) 
        { 
         nSockets.Add(handlerSocket); 
        } 
       } 
       ThreadStart thdstHandler = new 
       ThreadStart(handlerThreadList); 
       Thread thdHandler = new Thread(thdstHandler); 
       thdHandler.Start(); 
      } 
     } 
    } 
    //Handler Thread to receive list of files. 
    public void handlerThreadList() 
    { 

     Socket handlerSocketList = (Socket)nSockets[nSockets.Count - 1]; 
     NetworkStream networkStreams = new NetworkStream(handlerSocketList); 

     int requestRead = 0; 
     string dataReceived; 
     byte[] buffer = new byte[1024]; 
     //int iRx = soc.Receive(buffer); 
     requestRead = networkStreams.Read(buffer, 0, 1024); 
     char[] chars = new char[requestRead]; 

     System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder(); 
     int charLen = d.GetChars(buffer, 0, requestRead, chars, 0); 
     dataReceived = new System.String(chars); 

     Console.WriteLine(dataReceived); 
     MessageBox.Show(dataReceived); 

     //tcpListener.Stop(); 
     thdListener.Abort(); 


    } 

服務器代碼:

TcpListener tcpListener = new TcpListener(8080);   
    public void listenerThreadCommands() 
    { 

     tcpListener.Start(); 
     while (true) 
     { 
      handlerSocket = tcpListener.AcceptSocket(); 

      if (handlerSocket.Connected) 
      { 
       Control.CheckForIllegalCrossThreadCalls = false; 
       connections.Items.Add(
       handlerSocket.RemoteEndPoint.ToString() + " connected."); 
       // clientIP = handlerSocket.RemoteEndPoint.ToString(); 
       lock (this) 
       { 
        nSockets.Add(handlerSocket); 
       } 
       ThreadStart thdstHandler = new 
       ThreadStart(handlerThreadCommands); 
       Thread thdHandler = new Thread(thdstHandler); 
       thdHandler.Start(); 
       //tcpListener.Stop(); 


       //handlerSocket.Close(); 
      } 
     } 

    } 
    //Handler Thread to receive commands 
    public void handlerThreadCommands() 
    { 
     Socket handlerSocketCommands = (Socket)nSockets[nSockets.Count - 1]; 

     NetworkStream networkStream = new NetworkStream(handlerSocketCommands); 

     int requestRead = 0; 
     string dataReceived; 
     byte[] buffer = new byte[1024]; 
     requestRead = networkStream.Read(buffer, 0, 1024); 
     char[] chars = new char[requestRead]; 

     System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder(); 
     int charLen = d.GetChars(buffer, 0, requestRead, chars, 0); 
     dataReceived = new System.String(chars); 

     //connections.Items.Add(dataReceived); 
     if (dataReceived.Equals("list")) 
     { 
      localDate = DateTime.Now; 

      Files = Directory.GetFiles(System.IO.Directory.GetCurrentDirectory()) 
           .Select(Path.GetFileName) 
           .ToArray(); 
      String FilesString = ""; 
      for (int i = 0; i < Files.Length; i++) 
      { 
       FilesString += Files[i] + "\n"; 
      } 
      String clientIP = handlerSocketCommands.RemoteEndPoint.ToString(); 
      int index = clientIP.IndexOf(":"); 
      clientIP = clientIP.Substring(0, index); 
      WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list"); 
      Console.WriteLine(clientIP); 
      Console.WriteLine(FilesString); 

      Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(FilesString); 

      try 
      { 
       WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list-response"); 

       TcpClient clientSocket = new TcpClient(clientIP, 9090); 
       if (clientSocket.Connected) 
       { 

        NetworkStream networkStreamS = clientSocket.GetStream(); 
        networkStreamS.Write(bytesToSend, 0, bytesToSend.Length); 
        networkStreamS.Close(); 
        clientSocket.Close(); 
        networkStream.Close(); 
        //tcpListener.Stop(); 

        // handlerSocketAuthenticate.Close(); 

       } 
      } 
      catch 
      { 
       Console.WriteLine("Cant send"); 
      } 
     } 

     else if (dataReceived.Equals("downloadfile")) 
     { 
      // handlerSocketAuthenticate.Close(); 
      // tcpListener.Stop(); 
      networkStream.Close(); 
      thdListenerDownload = new Thread(new ThreadStart(listenerThreadDownloading)); 
      thdListenerDownload.Start(); 
     } 

     else 
     { 
      String clientIP1 = handlerSocketCommands.RemoteEndPoint.ToString(); 
      int index = clientIP1.IndexOf(":"); 
      clientIP1 = clientIP1.Substring(0, index); 
      // handlerSocketAuthenticate.Close(); 
      CommandExecutor(dataReceived, clientIP1); 
     } 
    } 
+1

如果你不能給我們足夠的代碼來找出問題所在,我們所能提供給你的只是同情。我不是說我們*會*。但至少有一種可能性。 –

+0

除非您爲客戶端和服務器添加代碼,否則任何人都無法幫助您。 – Gusman

+0

[從TCP服務器讀取 - 當用戶發送的數據進行兩次]的可能的複製(http://stackoverflow.com/q/35174983/11683) – GSerg

回答

2

有這麼多不同的事情錯了,你發佈的代碼,很難知道從哪裏開始,這是不可能的有信心在堆棧溢出的情況下,可以充分解決所有的缺陷。這就是說,出於幫助的目的,這似乎值得一試:

  1. 套接字是雙向的。客戶根本不需要使用TcpListener。 (按照慣例,「服務器」是「監聽」新連接的端點,「客戶端」是通過連接到監聽服務器啓動新連接的端點。)

    您應該只從一個連接客戶端到服務器,然後使用該套接字發送和接收服務器。
  2. 您正在將CheckForIllegalCrossThreadCalls財產設置爲false。這是邪惡的。發生的例外是幫助你。將該屬性設置爲false會禁用例外,但不會包含任何以防止例外被設計爲警告您的問題。

    你應該使用一些機制來確保當你訪問UI對象時,你只能在擁有這些對象的線程中這樣做。最原始的方法是使用Control.Invoke()。在現代C#中,最好使用async/await。使用TcpClient,這很簡單:您已經在使用GetStream()獲取代表套接字的NetworkStream對象,因此只需使用該對象上的異步方法(例如ReadAsync()),或者如果將流封裝爲StreamWriterStreamReader,則使用異步方法對象,如ReadLineAsync()
  3. 您正在檢查TcpClient對象的Connected屬性。這是毫無意義的。當Connect()方法返回時,您已連接。如果你不是,就會拋出異常。
  4. 您沒有充分同步訪問您的nSockets對象。特別是,您使用handlerThreadList()方法中的索引器。只有在確保沒有其他線程正在修改對象的情況下,才能同時使用該對象,這是安全的,在代碼中並非如此。
  5. 您正在使用ASCII編碼寫入流,但使用UTF8編碼進行讀取。在實踐中,這不是一個真正的問題,因爲ASCII僅包含0-127的代碼點,並且它們完全映射到UTF8中相同的字符代碼點。但它確實是不良形式。選擇一種編碼,堅持下去。
  6. 您正在接受使用AcceptSocket(),但只是將其包裝在NetworkStream中。爲什麼不使用AcceptTcpClient()並致電GetStream()SocketTcpClient都是很好的API,但在同一個程序中混合匹配有點奇怪,並且稍後可能會導致一些混淆,試圖保持正確的使用位置和原因。
  7. 您的代碼假定handlerThreadCommands()方法將始終按與接受連接的順序完全相同的順序調用。也就是說,你用nSockets[nSockets.Count - 1]檢索當前套接字。但是,由於Windows線程調度的工作原理,在任何一個旨在處理連接的線程開始之前,可能會接受兩個或多個連接,結果只處理最近的連接,並且它由多個線程處理。
  8. 您假定命令字符串將作爲完整單元接收。但這不是TCP的工作方式。 TCP只保證如果你收到一個字節,它將按照它之前發送的所有字節的順序排列。但是你可以接收任意數量的字節。特別是,你可以只接收一個字節,或者你可以接收多個相互連接的命令,或者你可以接收半個命令字符串,然後在另一半之後,或者一個命令的後半部分和前半部分接下來等等。實際上,這些問題並沒有在早期的測試中顯示出來,因爲服務器並不是在負載下運行,但後來他們可能會很好。代碼需要從一開始就設計好在這些條件下正常工作;以後嘗試修補不好的代碼要困難得多。

我不能說這是上面的代碼是唯一的錯誤,但它們是最明顯的,無論如何我認爲上述是足夠的食物供您思考。底線:你真的應該花更多的時間看看好的網絡示例,並真正理解它們是如何工作的,以及它們爲什麼以他們的方式編寫。您需要爲TCP協議的工作原理制定一個良好的心理模型,並確保您非常謹慎地遵守規則。

我推薦的一個資源是The Winsock Programmer's FAQ。很早以前,對於一個預先.NET的讀者而言,但使用更高級別的網絡API時,其中包含的大部分信息仍然非常相關。

或者,不要嘗試自己編寫低級網絡代碼。有許多更高級別的API使用各種序列化技術來編碼整個對象,併爲您處理所有較低級別的網絡傳輸機制,使您能夠專注於自己的程序中的增值功能,而不是嘗試重新發明輪子。

相關問題