具有大量客户端的聊天服务器

发布于 2024-08-22 18:14:50 字数 1811 浏览 1 评论 0原文

我读了一些 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 个客户端线程) )。我认为聊天服务器会因大量线程而产生开销。请帮我一个解决方案。谢谢。

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

I read some C# chat source code & I see that: on chat server with a lot of connected clients, server listener will run in a separated thread & each connected client will also run in a separated thread.
Code examples:

Start server & begin listening in a separated thread:

    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);
            }
        }

And a connection will also run in a separated thread:

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();
        }

So, if a chat server with 10000 connected clients, the chat server application will have 10002 threads (one main thread, one server thread & 10000 client threads). I think the chat server will be overhead with a big number of threads. Please help me a solution. Thanks.

UPDATE:
I believe chat examples are only for learning networking & they are not suitable in real-world model. Please give me a real-world solution. Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

笑,眼淚并存 2024-08-29 18:14:51

如果您使用 .Net Framework 2.0 SP2 或更高版本,则可以使用基于 IO 完成端口。在这种情况下,您不应该创建自己的线程,因为 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);
}

If you use .Net framework 2.0 SP2 or higher, than you may use new asyncrhronous sockets model based on IO Completion ports. In this case you shouldn't create your own threads, because IO Completion ports do all job for you.

Here some examples:

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);
}
几味少女 2024-08-29 18:14:51

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

A standard mechanism to ease the burden is known as selection, which can multiplex multiple Socket instances to watch for the ones that are ready to be read or written to. See this document: http://codeidol.com/csharp/csharp-network/Csharp-Network-Programming-Classes/Csharp-Socket-Programming/ and scroll down to the section on select().

冷月断魂刀 2024-08-29 18:14:51

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

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

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

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

只是不要认为您会启动一堆线程,这将挽救这一天。它很草率,并且仅适用于非常小的数字。

1) You'll NEVER want that many threads running - even if you could get them to run on your box (which you can't - each thread has a stack associated with it that takes real RAM and as you start more and more and more you'll run out of physical resources in your box and watch it blow up).

2) You'll want to look into thread pooling - using a smaller amount of threads to tackle a larger amount of work - typically reading from a queue of work that you try to get through as quickly as possible.

3) You'll want to look into io completion ports - a means of having a callback when io (likek a disk read or a network io) is waiting for you to take action - think of a thread (or pool of threads) dedicated to getting io notifications and then shoving the action to take for that io into a queue and then another pool of threads that take care of the actual messaging/logging/etc.

4) What happens when you scale beyond one machine? Which you hope to do if you're successful right? :-) Typically people dedicate a set of N machines to chat - then they hash based on a identifier for the user (think a GUID that represented the user - or a UserID/bigint depending on what corresponds to some internal authentication token that is consistent from login to login) which allows them to deterministically route the user's status/state information to a specific machine in that set of N boxes dedicated to messaging. So if a user that hashes to server N[2] needs to check if theri friends ar logged in it is easy to know for each of their friends exactly which machine their friend's status should be in because the backend consistently hashes those friends to the IM machine that corresponds to each userid hash. (i.e. you know just from the userid what server in the farm should be handling the IM status for that user.

Just dont' think you're gonna spin up a bunch of threads and that will save the day. It's sloppy and works only in very small numbers.

老娘不死你永远是小三 2024-08-29 18:14:51

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

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

To make the matter worse you would also have to communicate between some arbitrary number of threads (it's a chat server, people want to talk to each other, not themselves.) I would suggest looking into UDP - can be done with a single thread on the server and fits the network activity well - people rarely write more then couple of sentences at a time in chat exchanges, which is very convenient for size-limited UDP datagrams.

There are other approaches of course, but one sure thing though is that you will never be able to do thread per socket at that scale.

还给你自由 2024-08-29 18:14:51

我建议您阅读 MSDN 杂志上的这篇精彩文章
描述:

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

C# 和 代码VB网络

I suggest you to read this great article on MSDN Magazine.
Describing:

  • Threaded Server
  • Select-Based Server
  • Asynchronous Server

codes in C# & VB.Net

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文