74

I am trying to understand SocketChannels, and NIO in general. I know how to work with regular sockets and how to make a simple thread-per-client server (using the regular blocking sockets).

So my questions:

  • What is a SocketChannel?
  • What is the extra I get when working with a SocketChannel instead of a Socket.
  • What is the relationship between a channel and a buffer?
  • What is a selector?
  • The first sentance in the documentation is A selectable channel for stream-oriented connecting sockets.. What does that mean?

I have read the also this documentation, but somehow I am not getting it...

4

3 回答 3

59

ASocket是阻塞输入/输出设备。Thread如果底层缓冲区已满,它会使用它来阻止读取并可能阻止写入。因此,如果你的服务器有一堆 open ,你必须创建一堆不同的线程Socket

ASocketChannel是一种从套接字读取的非阻塞方式,因此您可以让一个线程同时与一堆打开的连接进行通信。这通过将一堆SocketChannels 添加到 a Selector,然后循环选择器的select()方法来工作,该方法可以通知您套接字是否已被接受、接收数据或关闭。这使您可以在一个线程中与多个客户端进行通信,而不会产生多线程和同步的开销。

Buffers 是 NIO 的另一个特性,它允许您通过读取和写入访问底层数据,以避免将数据复制到新数组中的开销。

于 2013-01-09T00:31:15.990 回答
19

现在NIO太老了,很少有人记得 Java 在 1.4 之前是什么样的,这是你需要知道的,才能理解NIO.

简而言之,直到 Java 1.3,所有 I/O 都是阻塞类型的。更糟糕的是,没有select()对多路 I/O 进行系统调用的模拟。因此,用 Java 实现的服务器别无选择,只能采用“每个连接一个线程”的服务策略。

Java 1.4 中引入的 NIO 的基本点是使传统 UNIX 风格的多路复用非阻塞 I/O 的功能在 Java 中可用。如果您了解如何使用一组文件描述符(通常是套接字)进行编程select()或检测 I/O 准备情况,那么您将在以下位置找到所需的服务:您将s 用于非阻塞 I/O 端点, 和s 用于 fdsets 或 pollfd 数组。具有线程池的服务器,或具有每个处理多个连接的线程,现在成为可能。这就是“额外”。poll()NIOSocketChannelSelector

ABuffer是非阻塞套接字 I/O 所需的字节数组,尤其是在输出/写入端。如果只能立即写入缓冲区的一部分,那么通过阻塞 I/O,您的线程将简单地阻塞,直到可以写入整个缓冲区。使用非阻塞 I/O,你的线程会得到一个写了多少的返回值,留给你来处理下一轮的剩余部分。ABuffer通过显式实现填充和排出的生产者/消费者模式来处理这些机械细节,可以理解您的线程和 JVM 的内核将同步。

于 2013-01-09T20:13:51.100 回答
6

即使你在使用SocketChannels,也需要使用线程池来处理channels

考虑一下场景,您只使用一个线程负责轮询select()和处理SocketChannels选定的 from Selectors,如果一个通道需要 1 秒的处理时间,并且队列中有 10 个通道,这意味着您必须等待 10 秒才能进行下一次轮询是无法忍受的。所以应该有一个用于通道处理的线程池。

从这个意义上说,我没有看到每个客户端线程阻塞套接字模式的巨大差异。主要区别在于NIO模式,任务更小,它更像是每个任务的线程,并且任务可以被读取,写入,业务流程等。有关更多详细信息,您可以查看Netty的实现NioServerSocketChannelFactory,其中正在使用一个 Boss 线程接受连接,并将任务分派到 Worker 线程池进行处理

如果您真的喜欢一个线程,那么底线是至少您应该拥有池化 I/O 线程,因为 I/O 操作通常比指令处理周期慢几个数量级,您不会想要宝贵的一个线程被 I/O 阻塞,这正是NodeJS所做的,使用一个线程接受连接,并且所有 I/O 都是异步的,并由后端 I/O 线程池并行处理

旧式线程每个客户端死了吗? 我不这么认为,NIO 编程很复杂,多线程自然不是邪恶的,请记住,现代操作系统和 CPU 在多任务处理方面变得越来越好,因此多线程的开销随着时间的推移变得越来越小。

于 2014-06-03T09:14:01.023 回答