【问题标题】:Explain weird behavior of C# TcpClient/TcpListener on NetCore解释 NetCore 上 C# TcpClient/TcpListener 的奇怪行为
【发布时间】:2017-03-08 13:56:21
【问题描述】:

我在 Windows 10 上运行 NetCore,我有两个程序 - 我在本地运行的服务器和客户端。

执行顺序如下:

  • 运行服务器 - 有一个处理客户端的循环
  • 运行客户端 - 客户端完成其工作并终止进程
  • 第二次运行客户端

此序列使用以下代码从服务器生成以下输出:

Waiting for client.
Reading message.
Incoming message: This message is longer than what
Sending message.
Closing connection.
Waiting for client.
Reading message.
Incoming message: This message is longer than what
Sending message.
Closing connection.
Waiting for client.

以及来自客户端的以下输出:

Connecting to server.
Sending message.
Reading message.
Incoming message: Thank you!


Connecting to server.
Sending message.
Reading message.

Unhandled Exception: System.AggregateException: One or more errors occurred. (Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.) ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

换句话说 - 第二次尝试运行客户端以异常结束。那是奇怪的部分。

这是我重现问题的最小代码示例。

服务器代码:

public static async Task StartServerAsync()
{
  TcpListener listener = new TcpListener(IPAddress.Any, 1234);
  listener.Server.NoDelay = true;
  listener.Server.LingerState = new LingerOption(true, 0);
  listener.Start();

  while (true)
  {
    Console.WriteLine("Waiting for client.");
    using (TcpClient client = await listener.AcceptTcpClientAsync())
    {
      client.NoDelay = true;
      client.LingerState = new LingerOption(true, 0);

      Console.WriteLine("Reading message.");
      using (NetworkStream stream = client.GetStream())
      {
        byte[] buffer = new byte[32];
        int len = await stream.ReadAsync(buffer, 0, buffer.Length);
        string incomingMessage = Encoding.UTF8.GetString(buffer, 0, len);
        Console.WriteLine("Incoming message: {0}", incomingMessage);


        Console.WriteLine("Sending message.");
        byte[] message = Encoding.UTF8.GetBytes("Thank you!");
        await stream.WriteAsync(message, 0, message.Length);
        Console.WriteLine("Closing connection.");
      }
    }
  }
}

客户端代码:

public static async Task StartClientAsync()
{
  using (TcpClient client = new TcpClient())
  {
    client.NoDelay = true;
    client.LingerState = new LingerOption(true, 0);

    Console.WriteLine("Connecting to server.");
    await client.ConnectAsync("127.0.0.1", 1234);

    Console.WriteLine("Sending message.");
    using (NetworkStream stream = client.GetStream())
    {
      byte[] buffer = Encoding.UTF8.GetBytes("This message is longer than what the server is willing to read.");
      await stream.WriteAsync(buffer, 0, buffer.Length);

      Console.WriteLine("Reading message.");
      int len = await stream.ReadAsync(buffer, 0, buffer.Length);
      string message = Encoding.UTF8.GetString(buffer, 0, len);

      Console.WriteLine("Incoming message: {0}", message);
    }
  }
}

奇怪的是,如果我们将客户端的消息从

"This message is longer than what the server is willing to read."

"This message is short."

客户端的两个实例都在没有崩溃的情况下完成并产生相同的输出。

请注意,如果我省略了设置 LingerState 的所有三行,则没有区别。如果我将 LingerState 设置为

,则行为也没有区别

new LingerState(true, 5)

如果我将 NoDelay 设置为 false,也没有区别。换句话说,在任一侧为 LingerState 和 NoDelay 设置任何值似乎对崩溃没有任何影响。防止崩溃的唯一方法是在服务器端从客户端读取整个输入。

这很奇怪,我想知道是否有人可以解释一下。我不确定它是否也适用于 .NET Framework,仅在 NetCore 1.0.0 和 Windows 10 上进行了测试。

【问题讨论】:

    标签: c# .net tcpclient .net-core tcplistener


    【解决方案1】:

    .NET Core 团队终于在 GitHub 上回答了这个问题。

    长答案-见https://github.com/dotnet/corefx/issues/13114的最后评论

    简答——

    行为是预期的,是设计使然。

    【讨论】:

      【解决方案2】:

      我已使用您的代码 sn-ps 并创建了一个实际可运行的 demo solution,但仍处于未应答状态。

      只需稍作改动即可获得预期的行为。在您的服务器端代码中,您需要确保实际读取整个字符串。调整以下几行:

      byte[] buffer = new byte[32];
      int len = await stream.ReadAsync(buffer, 0, buffer.Length);
      string incomingMessage = Encoding.UTF8.GetString(buffer, 0, len);
      Console.WriteLine("Incoming message: {0}", incomingMessage);
      

      与:

      StringBuilder sb = new StringBuilder();
      byte[] buffer = new byte[32];
      int len;
      do
      {
          len = await stream.ReadAsync(buffer, 0, buffer.Length);
          sb.Append(Encoding.UTF8.GetString(buffer, 0, len));
      } while (len == buffer.Length);
      Console.WriteLine("Incoming message: {0}", sb.ToString());
      

      只要看看the demo solution 的历史,你就会发现所有的变化都是微乎其微的。

      【讨论】:

      • 抱歉,The only way to prevent the crash is to read the whole input from the client on the server side. 的问题中已经写了,问题本身是关于为什么在未阅读整条消息时会崩溃。
      猜你喜欢
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      • 2017-04-30
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多