0 0

Netty关闭连接后引发的空指针异常25

我用netty做一个文件传输,当客户端下载文件时,服务端传文件。服务端传文件的代码如下:

final ChunkedStream chunkedStream = new ChunkedStream(fis);
					ctx.channel().writeAndFlush(chunkedStream).addListener(new ChannelFutureListener() {
						@Override
						public void operationComplete(ChannelFuture future) throws Exception {
							if(!future.isSuccess()) {
								logger.debug("File send timeout");
							}
							if (ctx != null ) {
								if (ctx.channel().isActive()) {
									System.out.println("close again?");
									ctx.close();
								}									
							}
							
							if (chunkedStream != null) {
								chunkedStream.close();
							}
							if (fis != null) {
								try {
									fis.close();
								} catch (IOException e) {
								}
							}
							
						}
					});

 

问题出现在以下场景:我写了一个下载超时控制的handler,当超过指定时间后,会触发一个我自定义的Exception。然后我在exceptionCaught里捕获它,并关闭连接。这时,服务端还继续着向客户端传文件,连接关闭后,上面代码的operationComplete方法会被触发,并且future.isSuccess()返回false。operationComplete方法里面的代码执行完后,就开始出错了。错误信息栈如下所示:

 

16:29:55.637-[WARN ] Lsr-FileTransferService-IoProcessor-73 DefaultPromise - An exception was thrown by io.netty.handler.stream.ChunkedWriteHandler$5.operationComplete()

java.lang.IllegalStateException: complete already: DefaultChannelPromise@82acef(failure(java.nio.channels.ClosedChannelException)

at io.netty.util.concurrent.DefaultPromise.setFailure(DefaultPromise.java:401) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelPromise.setFailure(DefaultChannelPromise.java:87) ~[netty-all-4.0.10.Final.jar:na]

at io.netty.handler.stream.ChunkedWriteHandler$PendingWrite.fail(ChunkedWriteHandler.java:354) ~[netty-all-4.0.10.Final.jar:na]

at io.netty.handler.stream.ChunkedWriteHandler$5.operationComplete(ChunkedWriteHandler.java:306) ~[netty-all-4.0.10.Final.jar:na]

at io.netty.handler.stream.ChunkedWriteHandler$5.operationComplete(ChunkedWriteHandler.java:301) ~[netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:621) [netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:548) [netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:407) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.ChannelOutboundBuffer.safeFail(ChannelOutboundBuffer.java:508) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.ChannelOutboundBuffer.remove(ChannelOutboundBuffer.java:296) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.ChannelOutboundBuffer.failFlushed(ChannelOutboundBuffer.java:440) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:550) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelPipeline$HeadHandler.close(DefaultChannelPipeline.java:1018) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelHandlerContext.invokeClose(DefaultChannelHandlerContext.java:560) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelHandlerContext.close(DefaultChannelHandlerContext.java:545) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelHandlerContext.close(DefaultChannelHandlerContext.java:423) [netty-all-4.0.10.Final.jar:na]

at cn.com.agree.afa.lsr.service.aft.codec.service.GetFile$2.exceptionCaught(GetFile.java:124) [classes/:na]

at io.netty.channel.DefaultChannelHandlerContext.invokeExceptionCaught(DefaultChannelHandlerContext.java:275) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.DefaultChannelHandlerContext.fireExceptionCaught(DefaultChannelHandlerContext.java:253) [netty-all-4.0.10.Final.jar:na]

at cn.com.agree.afa.lsr.service.aft.codec.FileReadTimeoutHandler.readTimeout(FileReadTimeoutHandler.java:147) [classes/:na]

at cn.com.agree.afa.lsr.service.aft.codec.FileReadTimeoutHandler.access$2(FileReadTimeoutHandler.java:145) [classes/:na]

at cn.com.agree.afa.lsr.service.aft.codec.FileReadTimeoutHandler$FileReadTimeoutTask.run(FileReadTimeoutHandler.java:171) [classes/:na]

at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38) [netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:123) [netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354) [netty-all-4.0.10.Final.jar:na]

at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:348) [netty-all-4.0.10.Final.jar:na]

at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101) [netty-all-4.0.10.Final.jar:na]

at java.lang.Thread.run(Thread.java:662) [na:1.6.0_43]

java.nio.channels.ClosedChannelException: null

 

 

我的猜测是这样的,ctx.close()调用一次setFailure方法,而operationComplete执行完后也会调用ctx的setFailure方法,但是这时它调用的ctx已经被关闭了。所以有了上面的空指针异常。

 

我的问题是:
1,上面的错误是什么原因呢?

2,有没有更好的方法来实现下载超时?

 

任何意见或看法都感激不尽,谢谢!!

2014年9月28日 16:49

1个答案 按时间排序 按投票排序

0 0

你不是已经分析出来了么...类似socket已经关闭了,然后再close,就会引发异常。

Channel channel = future.getChannel();
然后判断一下
channel.isOpen() && channel.isConnected() 如果可以再ctx.close()

不清楚你的ctx是什么,是ChannelHandlerContext么?无所谓了,如果之前的人工异常触发后,channel已经close了,这里就应该可以判断出来,从而你不用再次ctx.close了。


题外话,服务器向客户端写数据的时候,有没有类似这样的判断
channel != null && channel.isOpen() && channel.isConnected() && channel.isWritable()

按理说这次通讯的链路都断了,怎么会跑到 operationComplete 方法里...

2014年9月29日 18:12

相关推荐

Global site tag (gtag.js) - Google Analytics