C# 从封闭的网络流读取数据不会';不要引起任何例外

C# 从封闭的网络流读取数据不会';不要引起任何例外,c#,C#,我试图创建一个相当简单的客户机-服务器应用程序,但对于通信,我希望使用二进制序列化对象。通信本身似乎很好,但当我关闭客户端的流时,服务器并没有真正注意到它,而是继续尝试读取流 服务器端(类服务器,在单独的线程中执行): 监听连接 listener = new TcpListener(IPAddress.Parse("127.0.0.1"), this.Port); listener.Start(); while (!interrupted) { Console.WriteLine("Wa

我试图创建一个相当简单的客户机-服务器应用程序,但对于通信,我希望使用二进制序列化对象。通信本身似乎很好,但当我关闭客户端的流时,服务器并没有真正注意到它,而是继续尝试读取流

服务器端(类服务器,在单独的线程中执行):

监听连接

listener = new TcpListener(IPAddress.Parse("127.0.0.1"), this.Port);
listener.Start();
while (!interrupted)
{
    Console.WriteLine("Waiting for client");
    TcpClient client = listener.AcceptTcpClient();
    AddClient(client);
    Console.WriteLine("Client connected");
}
添加客户端:

    public void AddClient(TcpClient socket)
    {
        Client client = new Client(this, socket);
        this.clients.Add(client);
        client.Start();
    }
侦听消息(深入客户端类):

单元测试:

Server server = new Server(6666);
server.Start();

Thread.Sleep(500);

TcpClient client = new TcpClient("127.0.0.1", 6666);
var message = new IntroductionMessage();
byte[] arr = message.Serialize();

client.GetStream().Write(arr, 0, arr.Length);

Thread.Sleep(500);

Assert.AreEqual(1, server.Clients.Count);

client.GetStream().Close();
client.Close();

Thread.Sleep(1000);

Assert.AreEqual(0, server.Clients.Count);

server.Stop();

因此消息被正确读取,但是,当我关闭流时,反序列化器。反序列化(流)似乎不会引发任何异常。。。那么,是不是不应该以这种方式读取,还是应该以其他方式关闭客户端?

假设服务器中用于反序列化消息的流是a(这是TcpClient.GetStream()返回的流的类型),您应该做两件事:

  • 定义“连接结束”的特定消息。当服务器接收并反序列化此消息时,退出while循环。为了实现这一点,客户端显然需要在关闭其TcpClient连接之前发送这样的消息。(您可以选择一种以类似方式工作的不同机制——但为什么不使用已有的消息机制…)

  • 在NetworkStream上设置一个ReadTimeout,以便在连接丢失且客户端无法发送“connection ends”消息时,服务器将触发超时并意识到客户端已“死亡”

  • 服务器中用于侦听客户端消息的代码应类似于以下内容:

    //
    // Time-out after 1 minute after receiving last message
    //
    stream.ReadTimeOut = 60 * 1000;
    
    BinaryFormatter deserializer = new BinaryFormatter();
    
    try
    {
        while (!interrupted)
        {
            System.Diagnostics.Debug.WriteLine("Waiting for the message...");
            AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream);
            System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType());
    
            //
            // Exit while-loop when receiving a "Connection ends"  message.
            // Adapt this if condition to whatever is appropriate for
            // your AbstractMessage type.
            //
            if (msg == ConnectionEndsMessage) break;
    
            raiseEvent(msg);
        }
    }
    catch (IOException ex)
    {
        ... handle timeout and other IOExceptions here...
    }
    

    服务器从哪里获取流?服务器对象中的TcpListener实例?现在,我更新了question@Kamil:根据消息的原始大小和使用软件的(网络)环境,您必须选择一个相当大的超时时间(以适应糟糕的酒店互联网连接,甚至可能是糟糕的调制解调器连接,嗯…)。在我的示例中,我选择了一分钟超时。如果您的程序仅在LAN中运行(或在低延迟internet连接上运行),则您可以选择小于30秒的超时。在任何情况下,都不要仅依赖超时,并实现专用的“连接结束”消息。这将极大地帮助您排除问题,因为您不清楚问题是客户端还是服务器中的错误,还是internet/网络连接的问题。。。
    //
    // Time-out after 1 minute after receiving last message
    //
    stream.ReadTimeOut = 60 * 1000;
    
    BinaryFormatter deserializer = new BinaryFormatter();
    
    try
    {
        while (!interrupted)
        {
            System.Diagnostics.Debug.WriteLine("Waiting for the message...");
            AbstractMessage msg = (AbstractMessage)deserializer.Deserialize(stream);
            System.Diagnostics.Debug.WriteLine("Message arrived: " + msg.GetType());
    
            //
            // Exit while-loop when receiving a "Connection ends"  message.
            // Adapt this if condition to whatever is appropriate for
            // your AbstractMessage type.
            //
            if (msg == ConnectionEndsMessage) break;
    
            raiseEvent(msg);
        }
    }
    catch (IOException ex)
    {
        ... handle timeout and other IOExceptions here...
    }