【问题标题】:Java Chat ServerJava 聊天服务器
【发布时间】:2009-07-20 04:01:04
【问题描述】:

我正在编写一个基于 java 的聊天服务器,目前我的设计基于以下内容:- 当聊天室中的人发送消息时,服务器端的聊天室类会循环向房间中的每个参与者发送相同的消息。显然,这是一个糟糕的设计,因为网络调用是在循环中对单个参与者进行的。因此,例如,假设聊天室中有 10 个人。当一个用户发送消息时,聊天室类将循环发送相同的消息给所有 10 个人。如果说,循环中的第 5 个人的连接很糟糕,那么第 6 .. 10 个人看到消息的时间会受到影响。

如果我从每个房间的单播转移到多播,那么我如何获得每个聊天室的私有多播组 ip?此外,每个聊天室都有单独的组似乎有点过头了。 主要问题之一是当我通过循环回复房间中的用户时,通过套接字连接发送数据的方法被阻塞。因此,我在想如果我使用非阻塞 NIO 套接字,然后将消息循环发送给收件人,那会解决问题吗? 是否有其他巧妙的技巧可以优化将数据发送给房间内的接收者?

【问题讨论】:

  • 试着把它分成几段,以便人们阅读。

标签: java networking network-programming chat


【解决方案1】:

简单的实现是每个客户端使用两个线程。一个线程从套接字读取另一个线程写入套接字。如果您的客户很少,这会很好。您必须了解 NIO 才能处理许多客户。 (当线程模型不能很好地工作时,'many' 的意思。)

客户端的读取线程从套接字读取整条消息,并将其放入 ChatRoom 对象的队列中。聊天室有一个线程将消息从队列中取出并将它们放入客户端的队列中。客户端写入线程轮询其队列并将消息写入套接字。

ChatRoom 有一个线程来接受连接并创建客户端对象并将它们放入一个集合中。它有另一个线程来轮询其消息队列并将消息分发到客户端队列。

Apache Mina 有一个使用 NIO 的例子

【讨论】:

  • 每个客户端有 2 个线程并且线程轮询和休眠,VM 的上下文切换和线程调度的开销将随着客户端数量线性增长。我喜欢将它部署在诸如双核处理器之类的商用硬件上,我认为 vm 调度程序将使 CPU 忙于大多数可避免的工作
  • 您作为客户提到的唯一数字是 10。根据我的经验,您几乎可以使用任何硬件处理 10 个聊天。
  • @Maninder Batth 你见过mailinator.blogspot.com/2008/02/…
  • 设置一个很小的堆栈大小(例如 32k,Windows 上的最小值)确实有助于线程模型。每个连接 64k 或 10,000 个并发连接仅 640mb!在 Vista 笔记本电脑需要超过 2gb 的内存才能正常运行的世界中,这只是 1gb 的一小部分。
【解决方案2】:

我同意连续循环访问您的收件人是个坏主意。为此,您可以考虑使用 ThreadPool 来提供帮助。但是,我认为多播将是您最好的选择。它非常适合聊天室模型。您只需要发送一次,您的迭代方法就会得到解决。您可以通过在地址中指定不同的端口来获得唯一的组 ID。

【讨论】:

  • 我认为 Java 的 Multicast 只支持 UDP,这意味着如果没有来自接收方的某种 ACK,服务器无法判断消息传递是否成功。
  • 我正在尝试使其成为一个非常通用的产品,并且某些客户端网络可能不支持多播。因此,我对多播方法犹豫不决
  • @Zach Scrivena:我同意,多播效率的权衡是数据完整性的损失(数据排序和潜在的损失)。但是,对于聊天室之类的东西,我认为这是一种可行的交易。
【解决方案3】:

简单的方法是每个客户端连接使用两个线程。一个线程处理从客户端读取消息,另一个用于发送消息,从而可以同时发送/接收来自客户端的消息。

为避免在循环客户端连接以广播消息时进行网络调用,服务器线程应将消息添加到队列中以发送给客户端。 java.util.concurrent 中的 LinkedBlockingQueue 非常适合这一点。下面是一个例子:

/**
 * Handles outgoing communication with client
 */
public class ClientConnection extends Thread {
    private Queue<String> outgoingMessages = new LinkedBlockingQueue<String>(MAX_OUTGOING);
    // ...
    public void queueOutgoing(String message) {
        if (!outgoingMessages.offer(message)) {
            // Kick slow clients
            kick();
        }
    }

    public void run() {
        // ...
        while (isConnected) {
            List<String> messages = new LinkedList<String>();
            outgoingMessages.drainTo(messages);
            for (String message : messages) {
                send(message);
            }
            // ...
        }
    }
}

public class Server {
    // ...
    public void broadcast(String message) {
        for (ClientConnection client : clients) {
            client.queueOutgoing(message);
        }
    }
}

【讨论】:

  • 所以每台服务器的线程数大约为:- 人数 * 2。可以说,我想为每台聊天服务器至少服务 1000 人。另外,我希望每台硬件机器至少有 5 台服务器。还假设硬件是双核服务器。因此,在双核机器上,每台服务器有 2000 个线程,每台 5 台服务器有 10k 线程。任何时候至少有 50% 的线程处于活动状态,每个双核有 5k 个线程,我发现在上下文切换和线程调度上会花费太多时间。有没有cmets?
  • NPTL 可以处理很多连接。参考mailinator.blogspot.com/2008/02/…
猜你喜欢
  • 2021-07-23
  • 2015-09-25
  • 1970-01-01
  • 1970-01-01
  • 2012-10-27
  • 2011-08-13
  • 2017-07-06
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多