【问题标题】:C# client/server QuestionC# 客户端/服务器问题
【发布时间】:2011-11-23 07:25:41
【问题描述】:

我对这里发生的事情感到非常困惑。我一直在设置断点,但我似乎无法理解。基本上,我有一个客户端和一个服务器。我希望客户端发送两个单独的数据字符串。通过设置断点,我注意到我的客户实际上确实用适当的数据填充了两个字符串。但是,服务器永远不会看到第二个字符串。为什么会发生这种情况,我该如何解决?任何帮助都将不胜感激!以下是我的代码:

服务器:

    private static void HandleClientComm(object client)
    {
        /** creating a list which contains DatabaseFile objects **/
        List<DatabaseFile> theDatabase = new List<DatabaseFile>();

        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();

        StringBuilder response = new StringBuilder();
        byte[] message = new byte[4096];
        int bytesRead;

        do
        {
            bytesRead = 0;

            try
            {
                /*do
                {
                    bytesRead = clientStream.Read(message, 0, message.Length);
                    response.Append(Encoding.ASCII.GetString(message, 0, bytesRead));
                } while (clientStream.DataAvailable);*/

当我将此注释代码更改为 bytesRead = clientStream.Read(message, 0, 4096); 我得到一个 IOException 错误,内容如下:无法将数据写入传输连接:现有连接被远程主机强行关闭。因此,我将其更改为 do while 循环。我如何绕过这个 IOException 并接受第二个字符串?

                ASCIIEncoding encoder = new ASCIIEncoding();
                String file = encoder.GetString(message, 0, bytesRead);
                Menu.Insert(theDatabase, file); 
            }
            catch (Exception)
            {
                // A socket error has occured
                break;
            }

            if (bytesRead == 0)
            {
                // The client has disconnected from the server
                break;
            }
        } while (clientStream.DataAvailable);

        // Release connections
        clientStream.Close();
        tcpClient.Close();
    }

客户:

    static void Main(string[] args)
    {
        TcpClient client = new TcpClient();

        IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888);

        client.Connect(serverEndPoint);

        NetworkStream clientStream = client.GetStream();
        NetworkStream clientStream2 = client.GetStream();

        ASCIIEncoding encoder = new ASCIIEncoding();
        ASCIIEncoding encoder2 = new ASCIIEncoding();
        String text = System.IO.File.ReadAllText("FirstNames.txt");
        String text2 = System.IO.File.ReadAllText("LastNames.txt");

        byte[] buffer = encoder.GetBytes(text);

        Console.ReadLine(); 
        clientStream.Write(buffer, 0, buffer.Length);
        clientStream.Flush();
        Console.ReadLine();

        byte[] buffer2 = encoder2.GetBytes(text2);

        clientStream2.Write(buffer2, 0, buffer2.Length);
        clientStream2.Flush();
        Console.ReadLine(); 
    }
}

【问题讨论】:

    标签: c# tcp client-server tcpclient networkstream


    【解决方案1】:

    客户端和服务器之间的通信是这样发生的(注意,步骤的顺序只是为了说明目的,实际运行时的顺序可能会有所不同):

    1. 客户:client.Connect(serverEndPoint)
      服务器:HandleClientComm(newClient)

    2. 客户:clientStream.Write(buffer, 0, buffer.Length)
      服务器:bytesRead = clientStream.Read(message, 0, message.Length)
      请注意,Read 不能保证阅读整个消息。只退回目前收到的部分是完全可以的

    3. 客户:Console.ReadLine()
      服务器:while (clientStream.DataAvailable)
      流上没有数据 - 客户端没有发送任何数据。即使没有ReadLine,这也可能发生 - 在客户端再次发送数据之前有一段时间

    4. 服务器:tcpClient.Close()

    5. 客户端:clientStream2.Write(buffer2, 0, buffer2.Length)
      你可以在这里得到一个异常,或者不是 - 取决于服务器是否已经关闭连接,在任何情况下服务器不再读取。

    您需要定义自己的服务器和客户端都将遵守的消息协议。例如,您可以让客户端在发送完成后关闭连接:

    客户:

    using (var client = new TcpClient("localhost", 8888))
    using (var clientStream = client.GetStream())
    {
        var buffer = Encoding.ASCII.GetBytes( File.ReadAllText("FirstNames.txt") );
        clientStream.Write(buffer, 0, buffer.Length);
    
        buffer = Encoding.ASCII.GetBytes( File.ReadAllText("LastNames.txt") );
        clientStream.Write(buffer, 0, buffer.Length);
    }
    

    服务器:

    using (var tcpClient = (TcpClient)client)
    using (var clientStream = tcpClient.GetStream())
    {
        // Store everything that the client sends.
        // This will work if the client closes the connection gracefully
        // after sending everything
    
        var receivedData = new MemoryStream();
        clientStream.CopyTo(receivedData);
    
        var rawBytes = receivedData.ToArray();
        var file = Encoding.ASCII.GetString(rawBytes, 0, rawBytes.Length);
    
        Menu.Insert(theDatabase, file);
    }
    

    协议很简单,对于您的情况可能就足够了。但是,它存在一些问题,应该在生产代码中解决(例如,如果客户端发送太多数据、耗尽服务器内存、如果客户端在不关闭连接的情况下停止发送怎么办等)

    【讨论】:

      【解决方案2】:

      因为在您第一次致电客户后,while (clientStream.DataAvailable) 不再为真。

      【讨论】:

      • 问题是,当我用类似“bytesREad = clientStream.REad(message,0,4096); 我的客户端向我发送一个 IOException 错误,内容如下:无法向传输连接写入数据:现有连接被远程主机强行关闭。因此,我将其更改为 do while 循环。如何绕过此 IOException 并接受第二个字符串?
      • @BlueButtons:您需要一个内部循环,它只是旋转说do...while (!clientStream.DataAvailable) 并等待数据。一旦数据到来,它就会读取所有可用的数据,验证消息,然后停止读取数据或再次开始等待下一个块。
      • @mellamokb:我尝试了这个建议,但当我这样做时,我的程序似乎不接受任何东西。你的意思是在我的代码中写了这个:do{ bytesRead = clientStream.read(....); response.Append(....) do{ }while(!clientStream.dataAvailable); }while(clientStream.DataAvailable); ?这就是我尝试过的......
      • 一般来说,您的服务应该一直处于启动状态,您可以通过while(true) 进行旋转和收听。当然你可以选择while(shouldContinue)
      • @yuan,好的,我已经解决了这个问题,但我的主要问题是能够读取两个字符串。当我进行此更改时,我得到了 IOException - 我之前指出过......有什么想法吗?
      【解决方案3】:

      您正在退出服务器中的 client-recv 循环,只是因为 DatAvailable 为 false。这意味着如果客户端要发送一帧数据(您使用)并暂停,那么您的服务器此时将看到没有可用数据并断开连接,即使一秒钟后来自客户端的另一帧数据即将到来中。几乎总是,对话的结束是基于正在传递的数据的内容。您当然永远不能尝试依赖 DataAvailable 碰巧有一次是假的这一事实。

      作为后续,如果您提供有关所使用协议的更多信息,我们可以提供更多帮助。例如,如果协议是两个字符串在末尾使用 CR/LF 发送,那么服务器循环应该检查缓冲区以了解何时断开连接。

      【讨论】:

      • 问题是,当我用类似“bytesREad = clientStream.REad(message,0,4096); 我的客户端向我发送一个 IOException 错误,内容如下:无法向传输连接写入数据:现有连接被远程主机强行关闭。因此,我将其更改为 do while 循环。如何绕过此 IOException 并接受第二个字符串?
      猜你喜欢
      • 2016-03-30
      • 1970-01-01
      • 2020-07-15
      • 1970-01-01
      • 2011-05-26
      • 2015-08-29
      • 2012-07-04
      • 2011-06-06
      • 1970-01-01
      相关资源
      最近更新 更多