C#客户端/服务器问题

4
我很困惑这里到底发生了什么。我已经设置了断点,可是还是无法理解。基本上,我有一个客户端和一个服务器。我希望客户端发送两个独立的数据字符串。通过设置断点,我注意到我的客户端确实填充了适当的数据到两个字符串中。然而,服务器永远也不会看到第二个字符串。为什么会发生这种情况?怎么修复?任何帮助都将非常感激!以下是我的代码: 服务器:
    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(); 
    }
}
3个回答

2
客户端和服务器之间的通信如下所述(请注意,步骤的顺序仅为说明目的,实际运行时可能有所不同):
  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并接受第二个字符串? - BigBug
@mellamokb:我尝试了这个建议,但是当我这样做时,我的程序似乎不接受任何东西。你的意思是在我的代码中写这个吗:do{ bytesRead = clientStream.read(....); response.Append(....) do{ }while(!clientStream.dataAvailable); }while(clientStream.DataAvailable);?这就是我尝试的... - BigBug
通常情况下,您的服务应该一直处于运行状态,可以使用 while(true) 进行旋转和监听。当然,您也可以使用 while(shouldContinue) 来进行更高级的操作。 - Yuan
@Yuan:嗯,我想我实际上调用了两次。我有“NetworkStream clientStream = client.GetStream(); NetworkStream clientStream2 = client.GetStream();”,所以我有两个单独的流。不过还是谢谢你的回复。 - BigBug
嗨Yuan:感谢你的帮助,我似乎已经自己解决了这个问题。顺便说一下,代码中有两个NetworkStreams。它们只是一个在另一个下面。这是很多代码需要阅读 - 很容易迷失其中,对此很抱歉。再次非常感谢你!!! - BigBug
显示剩余3条评论

1

你在服务器中退出了客户端接收循环,只因为DataAvailable为false。这意味着,如果客户端发送了一帧数据(你消耗了它)并暂停,那么你的服务器将在此时看不到可用的数据并断开连接,即使稍后另一帧数据从客户端即将进入。几乎总是基于传递的数据内容来结束对话。你肯定不能仅仅依赖于DataAvailable恰好一次为false的事实。

作为跟进,如果你提供更多关于所使用协议的信息,我们可以提供更多的帮助。例如,如果协议是发送两个带有CR/LF结尾的字符串,则服务器循环应检查缓冲区以了解何时断开连接。


问题在于,当我用类似“bytesREad = clientStream.REad(message,0,4096) ”的代码段替换那部分代码时,我的客户端会向我发送一个IOException错误,错误信息如下:无法将数据写入传输连接:远程主机强制关闭了现有的连接。因此,我改成了do while循环。我该如何解决这个IOException并接受第二个字符串? - BigBug

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接