Java NIO:OP_ACCEPT和OP_READ之间的关系? - java

我正在为我的项目重写核心的NIO服务器网络代码,并且试图找出何时应该“存储”连接信息以备将来使用。例如,一旦客户端以通常的方式连接,我便为该连接的客户端存储并关联SocketChannel对象,以便我可以随时将数据写入该客户端。通常,我将客户端的IP地址(包括端口)用作映射到SocketChannel对象的HashMap中的键。这样,我可以轻松地查找他们的IP地址,并通过该SocketChannel异步向他们发送数据。

这可能不是最好的方法,但是它可行,并且该项目太大,无法更改其基本网络代码,尽管我会考虑建议。但是,我的主要问题是:

我应该在什么时候“存储” SocketChannel以备将来使用?一旦接受连接(通过OP_ACCEPT),我一直在存储对SocketChannel的引用。我认为这是一种有效的方法,因为我可以假设在发生OP_READ事件时映射条目已经存在。否则,每次发生OP_READ时,我都需要对HashMap进行计算量大的检查,并且很明显与OP_ACCEPT相比,客户端发生的更多。我猜我担心的是,可能会有一些连接被接受(OP_ACCEPT)但从不发送任何数据(OP_READ)。可能由于防火墙问题或客户端或网络适配器故障而可能。我认为这可能会导致“僵尸”连接处于非活动状态,但也永远不会收到关闭消息。

我重写网络代码的部分原因是,在极少数情况下,我得到的客户端连接已进入一种奇怪的状态。我在想我处理OP_ACCEPT与OP_READ的方式,包括我用来假定连接“有效”并可以存储的信息,可能是错误的。

抱歉,我的问题不是更具体,我只是在寻找确定SocketChannel是否真正有效的最佳,最有效的方法,因此我可以存储对此的引用。非常感谢您的帮助!

参考方案

如果使用选择器和非阻塞IO,则可能需要考虑让NIO自己跟踪通道与其状态数据之间的关联。调用SelectionKey.register()时,可以使用三参数形式传递“附件”。在将来的每一个时刻,该SelectionKey都将始终返回您提供的附件对象。 (这显然是受操作系统级API中“void * user_data”类型的参数启发的。)

该附件与密钥一起保留,因此是保存状态数据的便捷位置。令人高兴的是,从通道到键到附件的所有映射都已经由NIO处理,因此您无需进行簿记。簿记-类似于Map查找-确实会在IO响应器循环中造成伤害。

作为一项附加功能,您还可以稍后更改附件,因此,如果在协议的不同阶段需要不同的状态对象,则也可以在SelectionKey上跟踪该对象。

关于发现连接的奇数状态,使用NIO和选择器可能会给您带来一些麻烦。例如,一旦SelectionKey发出准备读取的信号,则下次其他线程调用select()时它将继续准备读取。因此,很容易以尝试读取套接字的多个线程结束。另一方面,如果您在进行读取时尝试注销要读取的键,则最终可能会出现线程错误,因为SelectionKeys及其感兴趣的操作只能由实际调用select()的线程来操纵。因此,总的来说,此API具有一些优势,要使所有状态处理正确无误。

哦,还有另一种可能,取决于谁先关闭套接字,您可能会或可能不会注意到关闭的套接字,除非您明确询问。我想不起来确切的细节了,但这是这样的:客户端半封闭套接字的末端,这并不表示选择键上有任何准备好的操作,因此套接字通道永远不会被读取。这会使客户端上的套接字处于TIME_WAIT状态。

作为最终建议,如果您要进行异步IO,那么我绝对推荐“面向模式的软件体系结构”(POSA)系列中的几本书。第2卷介绍了许多IO模式。 (例如,NIO非常适合第2卷中的Reactor模式,它解决了我上面提到的许多状态处理问题。)第4卷包含了这些模式,并将它们嵌入到一般分布式系统的更大环境中。这两本书都是非常宝贵的资源。

什么是“取决于系统的默认”线程池? - java

从 AsynchronousFileChannel API: 如果在未指定线程池的情况下创建了AsynchronousFileChannel,则该通道与与系统相关的默认线程池相关联,该默认线程池可以与其他通道共享。我在其他任何地方都没有看到该术语,也没有通过网络搜索找到任何具体的解释。与系统有关的默认线程池到底是什么?有什么特点?它们在系统之间可能会有什么不…

Java:正则表达式模式匹配器是否有大小限制? - java

我的模式类似于OR:“word1 | word2 | word3”我大约有800个字。可能有问题吗? 参考方案 您仅受记忆和理智的限制。 :)

Java:线程池如何将线程映射到可运行对象 - java

试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…