【问题标题】:C# TcpListener - client number limitedC# TcpListener - 客户端数量有限
【发布时间】:2017-08-13 11:50:39
【问题描述】:

尊敬的 Stack Overflow 社区,

我目前正在用 C# 开发一个在线 TCG。服务器在我自己的机器上运行得非常好(四核,八线程 Windows 7)。我可以毫无问题地连接 12 个客户端。但是当我在服务器上启动它时,客户端的最大数量变得受限于机器上处理器核心的数量。我在单核 Windows 2016 Server 上运行代码,无法连接多个客户端来启动游戏。然后我在四核 Windows 2012 Server 上运行服务器,只能连接四个客户端并启动两个游戏。在有人断开连接之前,任何进一步的客户都会被完全忽略。然后新客户端被接受,服务器崩溃。

服务器是带有 .NET Framework 4.0 的 MS Visual Studio 2015 Windows 窗体应用程序。 Windows Server 机器具有 .NET Framework 4.0,而我自己的机器具有 4.5 版本。

我很乐意提供您可能需要的任何其他信息并回答您的所有问题。

C# 中的服务器代码:

public void Init()
{
    try {
        server = new TcpListener(IPAddress.Any, port);
        server.Start();
        serverRunning = true;
        server.BeginAcceptTcpClient(AcceptTcpClient, server);
        OutputTB.Text = "server has been started on port " + port.ToString();
    }
    catch (Exception e) {
        OutputTB.Text = "socket error: " + e.Message;
    }
}

private void AcceptTcpClient(IAsyncResult newClient)
{
    if (!serverRunning)
        return;
    server.BeginAcceptTcpClient(AcceptTcpClient, server);

    TcpListener listener = (TcpListener)newClient.AsyncState;
    ServerClient client = new ServerClient(listener.EndAcceptTcpClient(newClient));
    Send("SWHO", client);
    connectedClients.Add(client);
    Invoke((MethodInvoker)delegate {
        ClientListBox.Items.Add("Waiting for authentification...");
    });

    NetworkStream stream = client.tcp.GetStream();
    StreamReader reader = new StreamReader(stream, true);
    while (client.connected && serverRunning) {
        if (stream.DataAvailable) {
            string data = reader.ReadLine();
            if (data != null)
                OnIncomingData(client, data);
        }
    }
    Invoke((MethodInvoker)delegate {
        if (client.authenticated)
            ClientListBox.Items.Remove(client.playerName);
        else
            ClientListBox.Items.Remove("Waiting for authentification...");
    });
    Send("SDISC", client);
    waitingClients.Remove(client);
    connectedClients.Remove(client);
    client.tcp.Close();
}

private void OnIncomingData()
{
    //process client data
}

【问题讨论】:

  • 我会从 AcceptTcpClient() 中提取身份验证部分。也许它无法释放线程,直到回调返回或其他东西。接受连接并离开那里。也许使用状态机或其他东西来表明您正在等待客户端进行身份验证,以便下次读取数据时,您知道如何处理它。
  • 您能否将其简化为仅接受连接的服务器而不接受其他任何内容(这样它仍然可以重现)?这将简化问题。顺便说一句,您是否重新配置了线程池?这有点线程池耗尽的味道,这是不寻常的(也是一个错误)。

标签: c# .net server tcplistener


【解决方案1】:

线程池耗尽!非常感谢 itsme86 和 usr 的有用提示。现在代码如下所示:

private void AcceptTcpClient(IAsyncResult ar)
{
    server.BeginAcceptTcpClient(AcceptTcpClient, server);
    Task.Run(() => ListenToIncomingData(IAsyncResult ar);
}

有一件事我很好奇:异步调用不也消耗线程吗?怎么有足够的资源来处理它,却没有足够的资源来处理未完成的回调?

【讨论】:

  • 一个更重要的问题是:为什么你的线程用完了?通常,运行时将它们生成数百个是没有问题的。异步 IO 根本不消耗线程,但 Task.Run 不是异步 IO。所以这确实会消耗您之前使用的同一个池中的一个线程。为什么它现在起作用很神秘。应该还有一些工作被饿死。
  • 更神秘的是旧版本在我的 Windows 上运行。该问题仅发生在服务器上。它们是虚拟机。也许这就是问题所在?
  • 这些虚拟机的内核可能较少,从而导致可用的线程较少。对我来说,这听起来像是你进程中的某个人做了 ThreadPool.SetMaxThreads(Env.ProcessorCount)。
  • 有趣的是,该限制仅在我留在 acceptClient() 回调中时适用。现在我让回调完成并在另一个函数中监听,这个问题不再发生。
猜你喜欢
  • 1970-01-01
  • 2021-08-12
  • 1970-01-01
  • 1970-01-01
  • 2017-06-14
  • 2020-07-08
  • 2012-06-26
  • 1970-01-01
  • 2018-09-18
相关资源
最近更新 更多