客户端发送消息后停止工作,服务器不等待多个数据到来

Client stops working after sending message and server doesn't wait for mutliple data to come

我最近开始学习计算机网络,并决定尝试 TCP/IP 服务器和客户端。它们都可以工作,但我在向服务器发送多个数据时遇到了问题。我让它看起来像客户端之间的聊天服务,但服务器只接受一个客户端并在发送数据后关闭连接,并且客户端在向服务器发送数据后出于某种原因停止响应(我认为问题来自服务器而不是客户端本身),没有错误消息,只有在我强制关闭客户端时才会在服务器端出现。 这就是我的服务器的样子...

static void Main(string[] args)
        {
            //User can define port
            Console.WriteLine("open a port:");
            string userInputPort = Console.ReadLine();

            //listening for connections
            TcpListener listener = new TcpListener(System.Net.IPAddress.Any, Convert.ToInt32(userInputPort));
            listener.Start();
            Console.WriteLine("listening...");

            while (true)
            {
                //waiting for client to connect to server
                Console.WriteLine("Waiting for connection...");

                //when user connects to server, server will accept any request
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine("Client Accepted");

                NetworkStream stream = client.GetStream();
                StreamReader streamR = new StreamReader(client.GetStream());
                StreamWriter streamW = new StreamWriter(client.GetStream());

                while (true)
                {
                    if(client.Connected)
                    {
                        if (stream.CanRead)
                        {
                            //buffer
                            byte[] buffer = new byte[1024];
                            stream.Read(buffer, 0, buffer.Length);
                            int recv = 0;
                            foreach (byte b in buffer)
                            {
                                if(b != 0)
                                {
                                    recv++;
                                }
                            }
                            string request = Encoding.UTF8.GetString(buffer, 0, recv);
                            Console.WriteLine("request recived: " + request);
                            streamW.Flush();
                        }
                    }
                }

            }
        }
    }
}

这就是客户的样子...

...
                try
                {
                    //try to connect
                    client = new TcpClient(textBoxIP.Text, Convert.ToInt32(textBoxPort.Text));  
                }
...
        static void sendMessage(string message, TcpClient client)
        {
            int byteCount = Encoding.ASCII.GetByteCount(message);
            byte[] sendData = new byte[byteCount];
            sendData = Encoding.ASCII.GetBytes(message);

            NetworkStream stream = client.GetStream();
            stream.Write(sendData, 0, sendData.Length);

            StreamReader streamReader = new StreamReader(stream);
            string respone = streamReader.ReadLine();

            stream.Close();
            client.Close();
        }

正如我所说,我仍在学习计算机网络,对此代码的任何评论都会有所帮助! 谢谢

                while (true)
                {
                    if(client.Connected)
                    {
                        if (stream.CanRead)
                        {
                           

我没有看到任何代码,如果 client.Connectedstream.CanRead 变为假,则退出外部 while 循环。所以,当客户端断开连接并且它们变为假时,在我看来服务器只是永远循环。

您至少应该进行所有错误处理(关闭所有必要的流)并跳出循环。

作为下一个问题,您的代码一次只能有一个客户端。如果客户端实际上没有关闭连接。我不确定正确的 C# 解决方案是什么,但我认为它为每个连接的客户端生成一个单独的线程。

如果您让自己了解您对所编写的代码的实际期望,这会有所帮助。在我看来,您做出了很多自动假设,但实际上并没有确保将它们放入您的代码中。

  1. 您的服务器最多只能接受一个客户端。不是一个客户一次,而是一个曾经。你永远不会退出你的阅读循环,所以在客户端断开连接后,你将进入一个美妙的无限忙碌循环。您的意图可能是在一个断开连接时为另一个客户提供服务,但这不是您正在做的。
  2. 您假定服务器将向客户端发送响应。但是您实际上从未发送任何回复!客户端要读取内容,服务器首先必须发送内容供客户端读取。
  3. 您假设客户端发送的字符串将以零结尾,或者 Read 的目标缓冲区将被清零。如果你想要零终止,你必须自己从客户端发送它 - StreamWriter 当然 不会那样做。字符串不是作为 规则 以零终止的——它只是一种在内存中表示字符串的 C 风格方式。你不应该假设缓冲区的内容 beyond 什么 Read 的 return 值告诉你是 returned.

这些可能是您忘记完全放入的问题。现在谈谈对 TCP 部分工作原理的错误假设。为清楚起见,我将说明 的方式,而不是不正确的假设。

  1. 单次写入可以导致另一侧多次读取,单次读取可以读取另一侧多次写入的数据。 TCP 不发送(和接收)消息,它处理流。如果流对您来说不够好,您需要在此基础上添加消息传递协议。
  2. Read returns 读取了多少字节。使用它来处理响应,而不是寻找零。当 Read return 为零时,表示连接已关闭,您也应该关闭您的一侧。这就是您所需要的,而不是所有 while (true)if (Connected)if (CanRead) - 循环直到 Read return 为零。处理您收到的数据。
  3. TCP 流比大多数流更难处理;它的行为非常不同,以至于使用像 StreamReader 这样的助手是危险的。您必须自己完成这项工作,或者获得更高抽象的库来处理网络。 TCP 非常 低级别。
  4. 您不能指望得到 Read 的回复。 TCP 使用连接,但它不会自己做任何事情来保持连接的活动,也不会在连接断开时发出通知——它是为与今天截然不同的互联网而设计的,它可以愉快地在服务中断数小时内幸存下来——因为只要您不尝试 发送 任何东西。如果客户端突然断开连接,服务器可能永远不会知道。

您还应该确保正确清理所有本机资源 - 尽可能使用 using 真的很有帮助。 .NET 最终会 清理,但对于 I/O 之类的东西,这通常会很危险。