2017-05-04 37 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使用各种序列化技术来编码整个对象,并为您处理所有较低级别的网络传输机制,使您能够专注于自己的程序中的增值功能,而不是尝试重新发明轮子。

相关问题