1

我正在用java构建一个同时与多个客户端通信的服务器,我们最初的方法是服务器监听来自客户端的连接,一旦接收到连接并创建了一个套接字,就会产生一个新线程处理与每个客户端的通信,即使用 ObjectInputStream 读取请求,执行所需的操作(从数据库中获取数据、更新数据等),并将响应发送回客户端(如果需要)。而服务器本身又回去收听更多的连接。

这暂时可以正常工作,但是这种方法并不是真正可扩展的,它适用于同时连接的少量客户端,但是由于每个客户端都会产生另一个线程,当连接的客户端过多时会发生什么立刻?

所以我的下一个想法是维护一个包含所有连接客户端(套接字对象和一些额外信息)的排序列表,使用 ThreadPool 遍历它们并读取它们发送的任何内容,如果收到消息然后将其放入由另一个工作线程的 ThreadPool 执行的队列,一旦工作人员完成其任务,如果需要响应,则发送它。

后面的 2 个步骤实现起来非常简单,问题是每个客户端实现的原始线程,我使用 ObjectInputStream.readObject() 来读取消息,并且这个方法阻塞直到有东西要读取,这很好方法,但我不能对新方法使用相同的东西,因为如果我阻塞每个套接字,我将永远无法到达列表中更靠后的那些。

所以我需要一种方法来检查在调用 readObject() 之前是否有任何要阅读的内容,到目前为止,我尝试了以下解决方案:

解决方案 1: 使用 ObjectInputStream.available() 检查是否有可读取的内容,此方法失败,因为此方法似乎总是返回 0,无论流中是否存在对象。所以这根本没有帮助。

解决方案2: 使用PushbackInputStream检查流中第一个未读字节是否存在,如果存在则将其推回并使用ObjectInputStream读取对象,如果不继续:

            boolean available;
            int b = pushbackinput.read();
            if (b==-1)
                  available = false;
            else
            {
                pushbackinput.unread(b);
                available = true;
            }
            if (available)
            {
            Object message= objectinput.readObject();
            // continue with what you need to do with that object
            }

这也被证明是无用的,因为如果没有要读取的输入, read() 也会阻塞。如果流已关闭,它似乎只返回 -1 选项。如果流仍然打开但为空,它只是阻塞,所以这与简单地使用 ObjectInputStream.readObject(); 没有什么不同;

任何人都可以提出一种实际可行的方法吗?

4

1 回答 1

1

这是一个很好的问题,而且你已经做了一些功课……但这需要回顾一些历史才能把事情做好。请注意,您的问题实际上更多与套接字级通信有关,而不是 ObjectInputStream:

过去做事最简单的方法是每个套接字有一个单独的线程。这在一定程度上是可扩展的,但线程昂贵且创建速度慢。

作为回应,对于大型系统,人们创建了线程池,并在有工作要做时为线程上的套接字提供服务。这很复杂。

Java 语言随后使用 java.nio 包进行了更改,该包引入了Selector和非阻塞 IO。这创造了一种可靠(尽管有时令人困惑)的方式来使用更少的线程来服务多个套接字。在您的情况下,这将无济于事,因为您想知道何时准备好读取完整的对象,而不是何时只有“某些”对象。

在此期间,“格局”发生了变化,Java 现在能够更有效地创建和管理线程。“当前”的想法是,再次为每个套接字分配一个线程会更好/更快、更容易……请参阅每个连接模型的 Java 线程与 NIO

在你的情况下,我建议你坚持使用线程每套接字模型,你会没事的。Java 可以扩展和处理比套接字更多的线程,所以你会没事的。

于 2013-11-01T12:40:58.667 回答