【问题标题】:Chat server with a lot of clients有很多客户的聊天服务器
【发布时间】:2011-01-19 22:20:51
【问题描述】:

我阅读了一些 C# 聊天源代码,我看到:在具有大量连接客户端的聊天服务器上,服务器侦听器将在单独的线程中运行,并且每个连接的客户端也将在单独的线程中运行。 代码示例:

启动服务器并开始在一个单独的线程中监听:

    public void StartListening()
        {

            // Get the IP of the first network device, however this can prove unreliable on certain configurations
            IPAddress ipaLocal = ipAddress;

            // Create the TCP listener object using the IP of the server and the specified port
            tlsClient = new TcpListener(1986);

            // Start the TCP listener and listen for connections
            tlsClient.Start();

            // The while loop will check for true in this before checking for connections
            ServRunning = true;

            // Start the new tread that hosts the listener
            thrListener = new Thread(KeepListening);
            thrListener.Start();
        }

private void KeepListening()
        {
            // While the server is running
            while (ServRunning == true)
            {
                // Accept a pending connection
                tcpClient = tlsClient.AcceptTcpClient();
                // Create a new instance of Connection
                Connection newConnection = new Connection(tcpClient);
            }
        }

而且连接也会在单独的线程中运行:

public Connection(TcpClient tcpCon)
        {
            tcpClient = tcpCon;
            // The thread that accepts the client and awaits messages
            thrSender = new Thread(AcceptClient);
            // The thread calls the AcceptClient() method
            thrSender.Start();
        }

因此,如果聊天服务器有 10000 个连接的客户端,则聊天服务器应用程序将有 10002 个线程(一个主线程、一个服务器线程和 10000 个客户端线程)。我认为聊天服务器会有大量线程的开销。请帮我一个解决方案。谢谢。

更新: 我相信聊天示例仅用于学习网络,它们不适合现实世界的模型。请给我一个现实世界的解决方案。谢谢。

【问题讨论】:

标签: c# architecture networking


【解决方案1】:

如果您使用 .Net framework 2.0 SP2 或更高版本,则可以使用基于 IO Completion ports 的新异步套接字模型。在这种情况下,您不应该创建自己的线程,因为 IO 完成端口会为您完成所有工作。

这里有一些例子:

tcpServer = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);


private void EndAcceptSocket(IAsyncResult asyncResult)
{
    TcpListener lister = (TcpListener)asyncResult.AsyncState;
    Socket sock = lister.EndAcceptSocket(asyncResult);
    //handle socket connection (you may add socket to you internal storage or something)

    //start accepting another sockets
    lister.BeginAcceptSocket(EndAcceptSocket, lister);


    SocketAsyncEventArgs e = new SocketAsyncEventArgs();
    e.Completed += ReceiveCompleted;
    e.SetBuffer(new byte[socketBufferSize], 0, socketBufferSize);
    sock.ReceiveAsync(e);
}


void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
    var sock = (Socket)sender;
    if (!sock.Connected)
    {
      //handle socket disconnection
    }
    var buf = new byte[size];
    Array.Copy(e.Buffer, buf, size);
    //handle received data

    //start reading new data
    sock.ReceiveAsync(e);
}

【讨论】:

    【解决方案2】:

    减轻负担的标准机制称为选择,它可以多路复用多个 Socket 实例以监视准备好读取或写入的实例。请参阅此文档:http://codeidol.com/csharp/csharp-network/Csharp-Network-Programming-Classes/Csharp-Socket-Programming/ 并向下滚动到 select() 部分。

    【讨论】:

    • select() 和非阻塞读/写是高性能、高客户端计数服务器的基础。
    【解决方案3】:

    1) 你永远不会希望运行那么多线程——即使你可以让它们在你的盒子上运行(你不能——每个线程都有一个与之关联的堆栈,它占用真正的 RAM,并且随着你开始更多越来越多的你会耗尽你盒子里的物理资源,然后看着它爆炸)。

    2) 您需要研究线程池 - 使用较少数量的线程来处理大量工作 - 通常从您尝试尽快完成的工作队列中读取。

    3) 您需要查看 io 完成端口 - 一种在 io(如磁盘读取或网络 io)等待您采取行动时进行回调的方法 - 考虑一个线程(或池线程)专用于获取 io 通知,然后将该 io 采取的操作推入队列,然后是另一个处理实际消息传递/日志记录/等的线程池。

    4) 当你扩展到一台机器之外会发生什么?如果你成功了,你希望做什么? :-) 通常人们会专门使用一组 N 台机器进行聊天 - 然后他们根据用户的标识符进行哈希处理(想想代表用户的 GUID - 或 UserID/bigint,具体取决于与某些一致的内部身份验证令牌对应的内容从登录到登录),这允许他们确定性地将用户的状态/状态信息路由到专用于消息传递的 N 个框中的特定机器。因此,如果一个散列到服务器 N[2] 的用户需要检查他们的朋友是否登录,那么他们的每个朋友很容易知道他们朋友的状态应该在哪台机器上,因为后端始终将这些朋友散列到 IM对应于每个用户 ID 哈希的机器。 (即,您仅从用户 ID 知道场中的哪个服务器应该处理该用户的 IM 状态。

    只是不要'认为你会旋转一堆线程,这将节省一天。它很草率,而且只在极少数情况下有效。

    【讨论】:

      【解决方案4】:

      更糟糕的是,您还必须在任意数量的线程之间进行通信(它是一个聊天服务器,人们想要互相交谈,而不是他们自己。)我建议研究 UDP - 可以通过服务器上的单线程并且非常适合网络活动 - 人们很少在聊天交流中一次写超过几句话,这对于大小有限的 UDP 数据报非常方便。

      当然还有其他方法,但可以肯定的是,您永远无法以这种规模为每个套接字执行线程。

      【讨论】:

      • 你的意思是简单的聊天源码不适合真实世界的模型。
      【解决方案5】:

      我建议您阅读 MSDN 杂志上的 this great article。 描述:

      • 线程服务器
      • 基于选择的服务器
      • 异步服务器

      C# 和 VB.Net 中的代码

      【讨论】:

        猜你喜欢
        • 2017-07-06
        • 1970-01-01
        • 1970-01-01
        • 2016-04-09
        • 1970-01-01
        • 2013-04-09
        • 2015-02-17
        • 1970-01-01
        • 2018-10-04
        相关资源
        最近更新 更多