IdleStateHandler是Netty爲咱們提供的檢測鏈接有效性的處理器,一共有讀空閒,寫空閒,讀/寫空閒三種監測機制。java
將其添加到咱們的ChannelPipline中,即可以用來檢測空閒。express
先經過一段代碼來學習下IdleStateHandler的用法:apache
ConnectStateHandler:(負責監測通道的各類狀態並處理空閒事件IdleStateEvent)bootstrap
1 package com.insaneXs.netty.idlestate; 2 3 import io.netty.channel.ChannelHandlerContext; 4 import io.netty.channel.ChannelInboundHandlerAdapter; 5 import io.netty.handler.timeout.IdleState; 6 import io.netty.handler.timeout.IdleStateEvent; 7 8 public class ConnectStateHandler extends ChannelInboundHandlerAdapter { 9 10 public ConnectStateHandler() { 11 super(); 12 } 13 14 @Override 15 public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 16 System.out.println("Channel Register"); 17 super.channelRegistered(ctx); 18 } 19 20 @Override 21 public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { 22 System.out.println("Channel Unregister"); 23 super.channelUnregistered(ctx); 24 } 25 26 @Override 27 public void channelActive(ChannelHandlerContext ctx) throws Exception { 28 System.out.println("Channel Active"); 29 super.channelActive(ctx); 30 } 31 32 @Override 33 public void channelInactive(ChannelHandlerContext ctx) throws Exception { 34 System.out.println("Channel Inactive"); 35 super.channelInactive(ctx); 36 } 37 38 @Override 39 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 40 System.out.println("Channel Read"); 41 super.channelRead(ctx, msg); 42 } 43 44 @Override 45 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 46 System.out.println("Channel Read Complete"); 47 super.channelReadComplete(ctx); 48 } 49 50 @Override 51 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 52 if(evt instanceof IdleStateEvent){ 53 if(((IdleStateEvent)evt).state().equals(IdleState.READER_IDLE)){ 54 System.out.println("Read Idle"); 55 ctx.close(); 56 } 57 }else{ 58 super.userEventTriggered(ctx, evt); 59 } 60 } 61 62 @Override 63 public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { 64 super.channelWritabilityChanged(ctx); 65 } 66 67 @Override 68 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 69 super.exceptionCaught(ctx, cause); 70 } 71 }
服務器代碼:promise
1 package com.insaneXs.netty.idlestate; 2 3 import io.netty.bootstrap.ServerBootstrap; 4 import io.netty.channel.ChannelInitializer; 5 import io.netty.channel.ChannelPipeline; 6 import io.netty.channel.EventLoopGroup; 7 import io.netty.channel.nio.NioEventLoopGroup; 8 import io.netty.channel.socket.nio.NioServerSocketChannel; 9 import io.netty.channel.socket.nio.NioSocketChannel; 10 import io.netty.handler.timeout.IdleStateHandler; 11 12 public class NettyServer { 13 14 public static void main(String[] args){ 15 EventLoopGroup boss = new NioEventLoopGroup(1); 16 EventLoopGroup work = new NioEventLoopGroup(4); 17 18 try { 19 ServerBootstrap bootstrap = new ServerBootstrap(); 20 bootstrap.group(boss,work) 21 .channel(NioServerSocketChannel.class) 22 .childHandler(new ChannelInitializer<NioSocketChannel>() { 23 @Override 24 protected void initChannel(NioSocketChannel ch) throws Exception { 25 ChannelPipeline pipeline = ch.pipeline(); 26 27 pipeline.addLast(new IdleStateHandler(30, 0, 0)); 28 pipeline.addLast(new ConnectStateHandler()); 29 } 30 }); 31 32 33 bootstrap.bind(8322).sync().channel().closeFuture().sync(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 } 38 }
測試客戶端代碼:緩存
1 package com.insaneXs.netty.common; 2 3 import io.netty.bootstrap.Bootstrap; 4 import io.netty.channel.ChannelInboundHandlerAdapter; 5 import io.netty.channel.EventLoopGroup; 6 import io.netty.channel.nio.NioEventLoopGroup; 7 import io.netty.channel.socket.nio.NioSocketChannel; 8 9 public class CommonNettyClient { 10 11 public static void main(String[] args){ 12 EventLoopGroup group = new NioEventLoopGroup(); 13 14 Bootstrap bootstrap = new Bootstrap(); 15 bootstrap.group(group) 16 .channel(NioSocketChannel.class) 17 .remoteAddress("localhost", 8322) 18 .handler(new ChannelInboundHandlerAdapter()); 19 20 bootstrap.connect(); 21 } 22 }
測試結果:服務器
從上面的輸出中咱們能夠看到Channel的狀態變化:app
1.鏈接創建時會從register -> active,less
2.當讀空閒時,咱們產生了一個空閒事件,當ConnectStateHandler捕獲這個事件後,會主動斷開鏈接。 socket
3.斷開時則是從inactive -> unregister。
接下來咱們學習下IdleStateHandler源碼:
1 /* 2 * Copyright 2012 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package io.netty.handler.timeout; 17 18 import io.netty.bootstrap.ServerBootstrap; 19 import io.netty.channel.Channel; 20 import io.netty.channel.Channel.Unsafe; 21 import io.netty.channel.ChannelDuplexHandler; 22 import io.netty.channel.ChannelFuture; 23 import io.netty.channel.ChannelFutureListener; 24 import io.netty.channel.ChannelHandlerContext; 25 import io.netty.channel.ChannelInitializer; 26 import io.netty.channel.ChannelOutboundBuffer; 27 import io.netty.channel.ChannelPromise; 28 29 import java.util.concurrent.ScheduledFuture; 30 import java.util.concurrent.TimeUnit; 31 32 / 33 * 34 * @see ReadTimeoutHandler 35 * @see WriteTimeoutHandler 36 */ 37 public class IdleStateHandler extends ChannelDuplexHandler { 38 private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1); 39 40 // Not create a new ChannelFutureListener per write operation to reduce GC pressure. 41 private final ChannelFutureListener writeListener = new ChannelFutureListener() { 42 @Override 43 public void operationComplete(ChannelFuture future) throws Exception { 44 lastWriteTime = ticksInNanos(); 45 firstWriterIdleEvent = firstAllIdleEvent = true; 46 } 47 }; 48 49 //是否觀察出站狀況;默認false 50 private final boolean observeOutput; 51 52 /*******三種超時狀況管理*********/ 53 //讀超時時間 54 private final long readerIdleTimeNanos; 55 //寫超時時間 56 private final long writerIdleTimeNanos; 57 //讀或寫超時時間 58 private final long allIdleTimeNanos; 59 60 //讀空閒定時任務,驗證是否讀超時 61 private ScheduledFuture<?> readerIdleTimeout; 62 //最後一次讀時間 63 private long lastReadTime; 64 //是否第一次讀超時 65 private boolean firstReaderIdleEvent = true; 66 67 //寫空閒定時任務,驗證是否寫超時 68 private ScheduledFuture<?> writerIdleTimeout; 69 //最後一次寫超時 70 private long lastWriteTime; 71 //是否第一次寫超時 72 private boolean firstWriterIdleEvent = true; 73 74 //讀或寫超時定時任務 75 private ScheduledFuture<?> allIdleTimeout; 76 //是否讀或寫超時 77 private boolean firstAllIdleEvent = true; 78 79 //處理器狀態: 0-無狀態, 1-初始化, 2-銷燬 80 private byte state; // 0 - none, 1 - initialized, 2 - destroyed 81 //讀狀態標誌 82 private boolean reading; 83 84 /****用於觀察輸出狀況*****/ 85 private long lastChangeCheckTimeStamp; 86 private int lastMessageHashCode; 87 private long lastPendingWriteBytes; 88 89 //設置超時時間;默認單位爲秒 90 public IdleStateHandler( 91 int readerIdleTimeSeconds, 92 int writerIdleTimeSeconds, 93 int allIdleTimeSeconds) { 94 95 this(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds, 96 TimeUnit.SECONDS); 97 } 98 99 //設置三種狀況超時時間與時間單位 100 public IdleStateHandler( 101 long readerIdleTime, long writerIdleTime, long allIdleTime, 102 TimeUnit unit) { 103 this(false, readerIdleTime, writerIdleTime, allIdleTime, unit); 104 } 105 106 //設置是否觀察輸出狀況,三種狀況超時時間以及時間單位 107 public IdleStateHandler(boolean observeOutput, 108 long readerIdleTime, long writerIdleTime, long allIdleTime, 109 TimeUnit unit) { 110 if (unit == null) { 111 throw new NullPointerException("unit"); 112 } 113 114 this.observeOutput = observeOutput; 115 116 if (readerIdleTime <= 0) { 117 readerIdleTimeNanos = 0; 118 } else { 119 readerIdleTimeNanos = Math.max(unit.toNanos(readerIdleTime), MIN_TIMEOUT_NANOS); 120 } 121 if (writerIdleTime <= 0) { 122 writerIdleTimeNanos = 0; 123 } else { 124 writerIdleTimeNanos = Math.max(unit.toNanos(writerIdleTime), MIN_TIMEOUT_NANOS); 125 } 126 if (allIdleTime <= 0) { 127 allIdleTimeNanos = 0; 128 } else { 129 allIdleTimeNanos = Math.max(unit.toNanos(allIdleTime), MIN_TIMEOUT_NANOS); 130 } 131 } 132 133 /************將時間轉換成毫秒級***************/ 134 public long getReaderIdleTimeInMillis() { 135 return TimeUnit.NANOSECONDS.toMillis(readerIdleTimeNanos); 136 } 137 138 139 public long getWriterIdleTimeInMillis() { 140 return TimeUnit.NANOSECONDS.toMillis(writerIdleTimeNanos); 141 } 142 143 144 public long getAllIdleTimeInMillis() { 145 return TimeUnit.NANOSECONDS.toMillis(allIdleTimeNanos); 146 } 147 148 //當處理器被添加時,視狀況決定是否初始化 149 @Override 150 public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 151 if (ctx.channel().isActive() && ctx.channel().isRegistered()) { 152 //判斷channel是否已經激活和註冊,避免 153 initialize(ctx); 154 } else { 155 // channelActive() event has not been fired yet. this.channelActive() will be invoked 156 // and initialization will occur there. 157 } 158 } 159 160 //移除時,調用destroy銷燬 161 @Override 162 public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { 163 destroy(); 164 } 165 166 //當通道被註冊時,視狀況決定是否初始化 167 @Override 168 public void channelRegistered(ChannelHandlerContext ctx) throws Exception { 169 // Initialize early if channel is active already. 170 if (ctx.channel().isActive()) { 171 initialize(ctx); 172 } 173 super.channelRegistered(ctx); 174 } 175 176 //當通道激活時,初始化 177 @Override 178 public void channelActive(ChannelHandlerContext ctx) throws Exception { 179 // This method will be invoked only if this handler was added 180 // before channelActive() event is fired. If a user adds this handler 181 // after the channelActive() event, initialize() will be called by beforeAdd(). 182 initialize(ctx); 183 super.channelActive(ctx); 184 } 185 186 //通道不活躍時,調用destroy銷燬 187 @Override 188 public void channelInactive(ChannelHandlerContext ctx) throws Exception { 189 destroy(); 190 super.channelInactive(ctx); 191 } 192 193 //讀事件 194 @Override 195 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 196 //開啓了讀空閒監測 或 讀寫空閒檢測 197 if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { 198 //修改成正在讀,並重置首次讀寫事件的標誌位爲true 199 reading = true; 200 firstReaderIdleEvent = firstAllIdleEvent = true; 201 } 202 ctx.fireChannelRead(msg); 203 } 204 205 //讀完成 206 @Override 207 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 208 //若是開啓了 讀/讀寫標誌 且 正在讀 209 if ((readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) && reading) { 210 //修改最後一次讀取時間,重置正在讀標識 211 lastReadTime = ticksInNanos(); 212 reading = false; 213 } 214 ctx.fireChannelReadComplete(); 215 } 216 217 @Override 218 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { 219 //若是開啓了 寫/讀寫標誌 220 if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) { 221 ctx.write(msg, promise.unvoid()).addListener(writeListener); 222 } else { 223 ctx.write(msg, promise); 224 } 225 } 226 227 //初始化 228 private void initialize(ChannelHandlerContext ctx) { 229 //判斷狀態避免重複初始化 230 switch (state) { 231 case 1: 232 case 2: 233 return; 234 } 235 236 state = 1; 237 //觀察輸出狀況 238 initOutputChanged(ctx); 239 240 //初始化最後一次讀寫時間 241 lastReadTime = lastWriteTime = ticksInNanos(); 242 243 //根據超時時間,判斷是否開啓超時監測 244 if (readerIdleTimeNanos > 0) { 245 readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx), 246 readerIdleTimeNanos, TimeUnit.NANOSECONDS); 247 } 248 if (writerIdleTimeNanos > 0) { 249 writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx), 250 writerIdleTimeNanos, TimeUnit.NANOSECONDS); 251 } 252 if (allIdleTimeNanos > 0) { 253 allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx), 254 allIdleTimeNanos, TimeUnit.NANOSECONDS); 255 } 256 } 257 258 /** 259 * This method is visible for testing! 260 */ 261 long ticksInNanos() { 262 return System.nanoTime(); 263 } 264 265 /** 266 * This method is visible for testing! 267 */ 268 ScheduledFuture<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) { 269 return ctx.executor().schedule(task, delay, unit); 270 } 271 272 //銷燬 273 private void destroy() { 274 //更改狀態 取消定時器 275 state = 2; 276 277 if (readerIdleTimeout != null) { 278 readerIdleTimeout.cancel(false); 279 readerIdleTimeout = null; 280 } 281 if (writerIdleTimeout != null) { 282 writerIdleTimeout.cancel(false); 283 writerIdleTimeout = null; 284 } 285 if (allIdleTimeout != null) { 286 allIdleTimeout.cancel(false); 287 allIdleTimeout = null; 288 } 289 } 290 291 /** 292 * Is called when an {@link IdleStateEvent} should be fired. This implementation calls 293 * {@link ChannelHandlerContext#fireUserEventTriggered(Object)}. 294 */ 295 protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { 296 ctx.fireUserEventTriggered(evt); 297 } 298 299 /** 300 * Returns a {@link IdleStateEvent}. 301 */ 302 protected IdleStateEvent newIdleStateEvent(IdleState state, boolean first) { 303 switch (state) { 304 case ALL_IDLE: 305 return first ? IdleStateEvent.FIRST_ALL_IDLE_STATE_EVENT : IdleStateEvent.ALL_IDLE_STATE_EVENT; 306 case READER_IDLE: 307 return first ? IdleStateEvent.FIRST_READER_IDLE_STATE_EVENT : IdleStateEvent.READER_IDLE_STATE_EVENT; 308 case WRITER_IDLE: 309 return first ? IdleStateEvent.FIRST_WRITER_IDLE_STATE_EVENT : IdleStateEvent.WRITER_IDLE_STATE_EVENT; 310 default: 311 throw new IllegalArgumentException("Unhandled: state=" + state + ", first=" + first); 312 } 313 } 314 315 /** 316 * @see #hasOutputChanged(ChannelHandlerContext, boolean) 317 */ 318 private void initOutputChanged(ChannelHandlerContext ctx) { 319 if (observeOutput) { 320 Channel channel = ctx.channel(); 321 Unsafe unsafe = channel.unsafe(); 322 ChannelOutboundBuffer buf = unsafe.outboundBuffer(); 323 //初始化消息內容的HashCode和byte 324 if (buf != null) { 325 lastMessageHashCode = System.identityHashCode(buf.current()); 326 lastPendingWriteBytes = buf.totalPendingWriteBytes(); 327 } 328 } 329 } 330 331 //判斷輸出是否有變化 332 private boolean hasOutputChanged(ChannelHandlerContext ctx, boolean first) { 333 if (observeOutput) { 334 335 // We can take this shortcut if the ChannelPromises that got passed into write() 336 // appear to complete. It indicates "change" on message level and we simply assume 337 // that there's change happening on byte level. If the user doesn't observe channel 338 // writability events then they'll eventually OOME and there's clearly a different 339 // problem and idleness is least of their concerns. 340 if (lastChangeCheckTimeStamp != lastWriteTime) { 341 lastChangeCheckTimeStamp = lastWriteTime; 342 343 // But this applies only if it's the non-first call. 344 if (!first) { 345 return true; 346 } 347 } 348 349 Channel channel = ctx.channel(); 350 Unsafe unsafe = channel.unsafe(); 351 ChannelOutboundBuffer buf = unsafe.outboundBuffer(); 352 353 if (buf != null) { 354 int messageHashCode = System.identityHashCode(buf.current()); 355 long pendingWriteBytes = buf.totalPendingWriteBytes(); 356 357 if (messageHashCode != lastMessageHashCode || pendingWriteBytes != lastPendingWriteBytes) { 358 lastMessageHashCode = messageHashCode; 359 lastPendingWriteBytes = pendingWriteBytes; 360 361 if (!first) { 362 return true; 363 } 364 } 365 } 366 } 367 368 return false; 369 } 370 371 //監測任務的父類 372 private abstract static class AbstractIdleTask implements Runnable { 373 374 private final ChannelHandlerContext ctx; 375 376 AbstractIdleTask(ChannelHandlerContext ctx) { 377 this.ctx = ctx; 378 } 379 380 @Override 381 public void run() { 382 if (!ctx.channel().isOpen()) { 383 return; 384 } 385 386 run(ctx); 387 } 388 389 protected abstract void run(ChannelHandlerContext ctx); 390 } 391 392 //讀監測定時任務 393 private final class ReaderIdleTimeoutTask extends AbstractIdleTask { 394 395 ReaderIdleTimeoutTask(ChannelHandlerContext ctx) { 396 super(ctx); 397 } 398 399 @Override 400 protected void run(ChannelHandlerContext ctx) { 401 long nextDelay = readerIdleTimeNanos; 402 if (!reading) {//不在讀的過程當中 403 //計算是否超時 404 nextDelay -= ticksInNanos() - lastReadTime; 405 } 406 407 if (nextDelay <= 0) {//已經超時 408 //下次空閒監測 409 readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS); 410 411 boolean first = firstReaderIdleEvent; 412 firstReaderIdleEvent = false; 413 414 try { 415 //出發讀超時事件 416 IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first); 417 channelIdle(ctx, event); 418 } catch (Throwable t) { 419 ctx.fireExceptionCaught(t); 420 } 421 } else { 422 // Read occurred before the timeout - set a new timeout with shorter delay. 423 readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); 424 } 425 } 426 } 427 428 //寫監測定時任務 429 private final class WriterIdleTimeoutTask extends AbstractIdleTask { 430 431 WriterIdleTimeoutTask(ChannelHandlerContext ctx) { 432 super(ctx); 433 } 434 435 @Override 436 protected void run(ChannelHandlerContext ctx) { 437 438 long lastWriteTime = IdleStateHandler.this.lastWriteTime; 439 long nextDelay = writerIdleTimeNanos - (ticksInNanos() - lastWriteTime); 440 if (nextDelay <= 0) {//寫超時 441 //下一次超時檢查時間 442 writerIdleTimeout = schedule(ctx, this, writerIdleTimeNanos, TimeUnit.NANOSECONDS); 443 444 boolean first = firstWriterIdleEvent; 445 firstWriterIdleEvent = false; 446 447 try { 448 //輸入內容是否發生變化:觀察輸出模式下且輸出內容發生變化則不認爲寫超時 449 if (hasOutputChanged(ctx, first)) { 450 return; 451 } 452 //傳遞寫超時事件 453 IdleStateEvent event = newIdleStateEvent(IdleState.WRITER_IDLE, first); 454 channelIdle(ctx, event); 455 } catch (Throwable t) { 456 ctx.fireExceptionCaught(t); 457 } 458 } else { 459 // Write occurred before the timeout - set a new timeout with shorter delay. 460 writerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); 461 } 462 } 463 } 464 465 //讀寫監測定時任務 466 private final class AllIdleTimeoutTask extends AbstractIdleTask { 467 468 AllIdleTimeoutTask(ChannelHandlerContext ctx) { 469 super(ctx); 470 } 471 472 @Override 473 protected void run(ChannelHandlerContext ctx) { 474 475 long nextDelay = allIdleTimeNanos; 476 if (!reading) { 477 nextDelay -= ticksInNanos() - Math.max(lastReadTime, lastWriteTime); 478 } 479 if (nextDelay <= 0) { 480 // Both reader and writer are idle - set a new timeout and 481 // notify the callback. 482 allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS); 483 484 boolean first = firstAllIdleEvent; 485 firstAllIdleEvent = false; 486 487 try { 488 if (hasOutputChanged(ctx, first)) { 489 return; 490 } 491 492 IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first); 493 channelIdle(ctx, event); 494 } catch (Throwable t) { 495 ctx.fireExceptionCaught(t); 496 } 497 } else { 498 // Either read or write occurred before the timeout - set a new 499 // timeout with shorter delay. 500 allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); 501 } 502 } 503 } 504 }
代碼的關鍵部分都已經給出了註釋。
這裏主要關注幾個問題,帶着這些問題去思考代碼設計的目的:
1.代碼中firstXXXIdleEvent的標誌位的做用是什麼?
2.ObserveOutput標誌位的做用是什麼?
3.reading的標誌的做用是什麼?
4.lastReadTime和lastWriteTime會在何時被重置?
首先來回答第一個問題,爲何會有firstXXXIdleEvent的標識。
這是由於一次讀寫事件中,可能會產生屢次的空閒超時事件。好比當我設置寫空閒時間爲5秒,而在某些狀況下,我整個寫過程須要30秒。那麼這一次寫過程就會產生多個寫空閒事件。firstXXXIdleEvent標誌位就是用來代表這次空閒事件是否爲第一次空閒事件。
知道了第一個問題後,咱們在看ObserveOutput標誌的做用。ObserveOutput標誌用來解決上述的慢輸出的問題。若是設置爲true,那麼即便寫過程當中發生了寫空閒事件,可是隻要hasOutputChanged方法判斷此時仍然在向外寫(寫緩存發生變化),那麼就不會爲這次超時產生寫超時事件。
那麼reading標誌的做用是什麼?reading的標誌是在read方法開始時,被設置爲true,在readComplete方法中又被設置爲false。也就是reading標誌表示正在發生讀的過程。
最後的問題,lastReadTime和lastWriteTime在何時被重置?lastReadTime和lastWriteTime會在相應的定時器中被用來和空閒時間做比較,以此來檢測在這段空閒時間中,是否發生過完整的讀或寫過程。所以,它們在handler初始化時被初始化值。lastReadTime會在readComplete時更新值。而lastWriteTime則是在WriterListener中設置(寫過程完成後的回掉中)。