套接字与SocketChannel
我想了解一般的SocketChannels和NIO 。 我知道如何使用常规套接字以及如何制作简单的每客户端服务器(使用常规阻塞套接字)。
所以我的问题:
- 什么是SocketChannel?
- 当使用SocketChannel而不是Socket时,额外获得的额外function是什么?
- 频道和缓冲区之间的关系是什么?
- 什么是select器?
- 文档中的第一个重要性是
A selectable channel for stream-oriented connecting sockets.
。 那是什么意思?
我也读过这个文件 ,但不知何故,我没有得到它…
一个Socket
是一个阻塞的input/输出设备。 它使正在使用它的Thread
在读取时阻塞,并且如果底层缓冲区已满,还可能阻塞写入。 因此,如果你的服务器有一堆开放的Socket
,你必须创build一堆不同的线程。
SocketChannel
是一个从套接字读取的非阻塞方式,因此您可以让一个线程同时与一组打开的连接进行通信。 这是通过向Selector
添加一堆SocketChannel
来实现的,然后循环select器的select()
方法,如果套接字已被接受,接收到数据或closures,则可以通知您。 这使您可以在一个线程中与多个客户端进行通信,而不会产生多个线程和同步的开销。
Buffer
s是NIO的另一个function,它允许您从读写访问底层数据,以避免将数据复制到新arrays的开销。
现在NIO
已经很老了,很less有人记得1.4之前的Java是什么,为了理解NIO
的“为什么”,你需要知道这一点。
简而言之,直到Java 1.3,所有I / O都是阻塞types。 更糟糕的是, select()
系统调用没有模拟多路复用I / O。 结果,用Java实现的服务器别无select,只能采用“单线程连接”服务策略。
在Java 1.4中引入的NIO的基本点是使Java中可用的传统UNIX式多路复用非阻塞I / O的function。 如果你知道如何用select()
或poll()
来编程来检测一组文件描述符(通常是套接字poll()
I / O准备情况,那么你将在NIO
find你需要的服务:你将使用SocketChannel
用于非阻塞I / O端点,以及用于fdsets或pollfd数组的Selector
。 具有线程池的服务器或者每个线程处理多个连接的线程现在变得可能了。 这是“额外”。
Buffer
是非阻塞套接字I / O所需要的一种字节数组,特别是在输出/写入方面。 如果只有一部分缓冲区可以立即写入,阻塞I / O,那么线程将被阻塞,直到整个文件被写入。 使用非阻塞I / O,您的线程将获得写入内容的返回值,留给您来处理下一轮的剩余内容。 Buffer
通过明确地实现一个生产者/消费者模式来填充和排空,从而处理这些机械细节,并且理解你的线程和JVM的内核不会同步。
即使你使用SocketChannels
,也需要使用线程池来处理channels
。
思考一下,你只使用一个线程来负责轮询select()
和处理从Selectors
的SocketChannels
,如果一个通道需要1秒的处理时间,并且有10个通道在队列中,这意味着你必须等待10个在下一轮投票之前几秒钟,这是无法忍受的。 所以应该有一个线程池用于通道处理。
从这个意义上说,我没有看到每个客户端的线程阻塞套接字模式的巨大差异。 主要区别在于NIO
模式,任务比较小,更像是每个任务的线程,任务可以是读,写,批处理等更多细节,可以看看Netty的实现NioServerSocketChannelFactory
,它使用一个Boss线程接受连接,并将任务分派给一个Worker线程池进行处理
如果你真的喜欢一个线程,底线至less是你所拥有的I / O线程池,因为I / O操作通常比指令处理周期慢得多,你不会想要珍贵的一个线程被I / O阻塞,这正是NodeJS所做的,使用一个线程接受连接,并且所有的I / O都是asynchronous的并且由后端I / O线程池并行处理
老式的线程每客户端死亡? 我不这么认为,NIO编程是复杂的,multithreading不是自然的邪恶。请记住,现代操作系统和CPU在多任务中越来越好,所以multithreading的开销随着时间的推移而变小。