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

Posted

技术标签:

【中文标题】发送消息后客户端停止工作,服务器不等待多个数据到来【英文标题】:Client stops working after sending message and server doesn't wait for mutliple data to come 【发布时间】:2021-12-06 18:47:03 【问题描述】:

我最近开始学习计算机网络并决定尝试 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();
        

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

【问题讨论】:

因此,当您调试每个专业、行和状态时。你得到了什么结果,程序与你的期望有什么不同?如果你不能回答这个问题,那么你在调试代码方面做得还不够。编写复杂软件时需要掌握事实,需要确定它的运行方式,猜测会以失败告终,以及在***上无法回答的问题 【参考方案1】:

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

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

大概是你忘记放入的东西的问题。现在谈谈关于 TCP 工作原理的部分错误假设。为了清楚起见,我将告诉它的方式,而不是错误的假设。

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

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

【讨论】:

【参考方案2】:
                while (true)
                
                    if(client.Connected)
                    
                        if (stream.CanRead)
                        
                           

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

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

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

【讨论】:

以上是关于发送消息后客户端停止工作,服务器不等待多个数据到来的主要内容,如果未能解决你的问题,请参考以下文章

同步消息发送和接收 AGSCMPP

websocket

从服务器向java中的多个客户端发送消息

消息总线:发件人必须等待来自多个收件人的确认

Spring 消息

SignalR 广播消息在停止/启动后重复