套接字与套接字通道


67

我试图理解SocketChannelsNIO。我知道如何使用常规套接字,以及如何制作一个简单的每客户端线程服务器(使用常规阻塞套接字)。

所以我的问题是:

  • 什么是SocketChannel?
  • 使用SocketChannel而不是Socket时,我得到的额外收益是什么。
  • 通道和缓冲区之间是什么关系?
  • 什么是选择器?
  • 文档中的第一句话是A selectable channel for stream-oriented connecting sockets.。这意味着什么?

我也阅读了本文档,但是不知何故……


8
对于那些因背景而不是内容而不满意您的问题的人,我必须表示歉意。作为研究生,我完全理解您何时被迫提供与您的研究领域不完全相同的课程,尤其是当您的资金依赖于该课程时。我认为您来这里进行澄清也很好。
Andrew Mao

Answers:


54

ASocket是阻止输入/输出设备。Thread如果基础缓冲区已满,它将使正在使用它的阻止读取,甚至可能阻止写入。因此,如果服务器具有一堆open,则必须创建一堆不同的线程Socket

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

Buffers是NIO的另一个功能,它使您可以通过读取和写入访问基础数据,从而避免了将数据复制到新阵列中的开销。


非常感谢。但是我还有一些遗漏之处。使用没有选择器的通道有什么优势吗?还是他们走到一起?
Ramzi Khahil 2013年

1
没有选择器就不能使用通道,例如,选择器会告诉您何时可以读取通道。我想不出您为什么要自己使用频道的原因,因为您基本上会重新实现选择器的功能。
安德鲁·毛

1
对不起,但是SocketChannel以哪种方式是非阻塞的?Selector.select()是一个阻塞操作。您说过select()会通知,但实际上它只是阻塞了。不能将其称为通知,因为它不使用回调。
ZhekaKozlov 2014年

10
术语只有在被滥用时才会造成混淆。“ NIO”中的“非阻塞”属于“ IO”,即I / O操作。线程不是“浪费”的,因为除了I / O之外它什么都不做。而且非阻塞I / O与异步I / O不同:这是完全不同的主题。
2014年

2
@AndrewMao您确实可以使用不带选择器的通道,即使在非阻塞模式下也可以使用,这不是默认设置,尽管不建议这样做。“ NIO”中的“ N”代表“ New”。
user207421 '17

17

到目前为止,它NIO是如此古老,以至于几乎没有人记得1.4之前的Java是什么,为了理解的“原因”,您需要了解Java NIO

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

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

ABuffer是非阻塞套接字I / O所需的字节数组,尤其是在输出/写入端。如果仅缓冲区的一部分可以立即写入,则在阻塞I / O的情况下,线程将一直阻塞,直到可以写入全部为止。使用非阻塞I / O,您的线程将获得已写入多少的返回值,由您自己来处理下一轮的剩余事务。ABuffer通过显式实现用于填充和排空的生产者/消费者模式来处理此类机械细节,可以理解,您的线程和JVM的内核将不会同步。


1
SocketChannel正在阻止。至少read()andwrite()方法是。的真正的无阻塞信道AsynchronousSocketChannel,这是在Java 7中引入
ZhekaKozlov

2
真?那么自Java 1.4引入NIO以来,configureBlocking(bool)方法发生了什么?
arayq2 2014年

3
听起来您已经将非阻塞I / O与异步I / O混淆了。即使在异步I / O中,阻塞也不能“完全消除”-例如,Future <>上的get()是阻塞调用。设计问题不是是否要块(你必不可少的混乱),但是阻塞。
arayq2 2014年

1
向后兼容?叹。随你。
arayq2 2014年

1
@ZhekaKozlov ASocketChannel在非阻塞模式下是非阻塞的。异步I / O是另一种野兽,与非阻塞模式无关。
user207421 '17

5

即使您正在使用SocketChannels,也必须使用线程池进行处理channels

考虑到scenairo,您仅使用一个线程负责轮询select()和处理SocketChannels从中选择的线程Selectors,如果一个通道处理需要1秒,并且队列中有10个通道,则意味着您必须等待10秒才能进行下一次轮询是无法忍受的。因此应该有一个线程池用于通道处理。

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

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

旧样式的“每客户线程数”是否已停用? 我不这么认为,NIO编程很复杂,并且多线程并不是天生的邪恶,请记住,现代操作系统和CPU在多任务处理方面变得越来越好,因此多线程的开销随着时间的推移而变得越来越小。


如果一个通道需要1秒钟进行处理,则不应使用NIO。
约翰

@John你会用什么?
El Mac

@ElMac显然不仅仅是一个线程。读/写套接字通道仍可以实现为多路复用单线程。但是数据处理应该在其他线程池中完成。
bash0r
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.