Java NIO持续触发读事件的解决方法

本文描述了在Java NIO中遇到的持续触发读事件的问题及其原因。当硬件设备第二次发送报文时,程序的Java NIO模块会持续触发读事件。问题源于未正确处理ByteBuffer,导致数据未被完全读取。通过理解NIO的水平触发机制,找到了问题的根源:ByteBuffer的limit和position相等,使得通道中的数据未被完全读取。解决方法包括使用ByteBuffer的compact()方法或在读取完整数据后将其置为null。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 现象

最近在一次项目编码中遇到这么一个情况: 程序本身是一个中间处理器的角色,功能可简述为接收硬件设备的报文,然后对某些报文处理(其他报文直接转发),然后发送服务端处理;服务端处理后回复,程序接收服务端回复,然后直接转发给硬件设备。在测试时发现,硬件设备第一次发送报文,能够正常处理,但是当硬件设备第二次发送报文时,程序中Java NIO会持续触发读事件,导致服务端持续回复报文,从而设备持续收到回复报文。

2. 问题排查

由于程序中很多地方使用到线程池、队列等,一开始排查是否是这些因素导致的,但是经过仔细的控制变量的测试,发现并不是这些导致的。在这个问题上纠结了半天多,感觉崩溃的时候,决定去网上找找思路,然后看到一个同仁遇到类似问题,其中说到NIO中的边缘触发和水平触发,于是仔细看了下这两个概念:
(1) 水平触发是当就绪的fd未被用户进程处理,下一次select()查询依旧会返回,这是select和poll的触发方式。
(2) 边缘触发是无论就绪的fd是否被处理,下一次不再返回。理论上性能更高,但是实现相当复杂,并且任何意外的丢失事件都会造成请求处理错误。epoll默认使用水平触发,通过相应选项可以使用边缘触发。

由于开发、测试是在windows平台下进行的,windows中Java NIO的底层网络模型是select模型,所以其触发方式为水平触发。所以上面遇到的情况很可能是由于就绪的fd未被处理,导致每次都会返回该事件。于是沿着这个方向排查。

经过排查,最终发现了原因:由于服务端在处理读事件时,将通道中的数据读到ByteBuffer中。将数据写入ByteBuffer后,经过一系列处理,该ByteBuffer的limit和position相等了,第二次再使用这个ByteBuffer来读通道中的数据时,将无法将数据写入该ByteBuffer,即就绪fd未被处理,导致下次selector调用select()方法时,又会返回该事件。

3. 解决方法

找到

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值