Netty 中 IOException Connection reset by peer 与 javaniochannelsClosedChannelException null.docx
- 文档编号:17647982
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:10
- 大小:17.71KB
Netty 中 IOException Connection reset by peer 与 javaniochannelsClosedChannelException null.docx
《Netty 中 IOException Connection reset by peer 与 javaniochannelsClosedChannelException null.docx》由会员分享,可在线阅读,更多相关《Netty 中 IOException Connection reset by peer 与 javaniochannelsClosedChannelException null.docx(10页珍藏版)》请在冰点文库上搜索。
Netty中IOExceptionConnectionresetbypeer与javaniochannelsClosedChannelExceptionnull
Netty中IOException:
Connectionresetbypeer与java.nio.channels.ClosedChannelException:
null
最近发现系统中出现了很多IOException:
Connectionresetbypeer与ClosedChannelException:
null
深入看了看代码,做了些测试,发现Connectionreset会在客户端不知道channel被关闭的情况下,触发了eventloop的unsafe.read()操作抛出
而ClosedChannelException一般是由Netty主动抛出的,在AbstractChannel以及SSLHandler里都可以看到ClosedChannel相关的代码
AbstractChannel
staticfinalClosedChannelExceptionCLOSED_CHANNEL_EXCEPTION=newClosedChannelException();
...
static{
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
NOT_YET_CONNECTED_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
}
...
@Override
publicvoidwrite(Objectmsg,ChannelPromisepromise){
ChannelOutboundBufferoutboundBuffer=this.outboundBuffer;
if(outboundBuffer==null){
//IftheoutboundBufferisnullweknowthechannelwasclosedandso
//needtofailthefuturerightaway.Ifitisnotnullthehandlingoftherest
//willbedoneinflush0()
//See
safeSetFailure(promise,CLOSED_CHANNEL_EXCEPTION);
//releasemessagenowtopreventresource-leak
ReferenceCountUtil.release(msg);
return;
}
outboundBuffer.addMessage(msg,promise);
}
在代码的许多部分,都会有这个ClosedChannelException,大概的意思是说在channelclose以后,如果还调用了write方法,则会将write的future设置为failure,并将cause设置为ClosedChannelException,同样SSLHandler中也类似
-----------------
回到Connectionresetbypeer,要模拟这个情况比较简单,就是在server端设置一个在channelActive的时候就closechannel的handler.而在client端则写一个Connect成功后立即发送请求数据的listener.如下
client
publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{
Bootstrapb=newBootstrap();
b.group(newNioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(newChannelInitializer
@Override
protectedvoidinitChannel(NioSocketChannelch)throwsException{
}
});
b.connect("localhost",8090).addListener(newChannelFutureListener(){
@Override
publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(future.isSuccess()){
future.channel().write(Unpooled.buffer().writeBytes("123".getBytes()));
future.channel().flush();
}
}
});
server
publicclassSimpleServer{
publicstaticvoidmain(String[]args)throwsException{
EventLoopGroupbossGroup=newNioEventLoopGroup
(1);
EventLoopGroupworkerGroup=newNioEventLoopGroup();
ServerBootstrapb=newServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_REUSEADDR,true)
.childHandler(newChannelInitializer
@Override
protectedvoidinitChannel(NioSocketChannelch)throwsException{
ch.pipeline().addLast(newSimpleServerHandler());
}
});
b.bind(8090).sync().channel().closeFuture().sync();
}
}
publicclassSimpleServerHandlerextendsChannelInboundHandlerAdapter{
@Override
publicvoidchannelActive(ChannelHandlerContextctx)throwsException{
ctx.channel().close().sync();
}
@Override
publicvoidchannelRead(ChannelHandlerContextctx,finalObjectmsg)throwsException{
System.out.println(123);
}
@Override
publicvoidchannelInactive(ChannelHandlerContextctx)throwsException{
System.out.println("inactive");
}
}
这种情况之所以能触发connectionresetbypeer异常,是因为connect成功以后,client段先会触发connect成功的listener,这个时候server段虽然断开了channel,也触发channel断开的事件(它会触发一个客户端read事件,但是这个read会返回-1,-1代表channel关闭,client的channelInactive跟channel active状态的改变都是在这时发生的),但是这个事件是在connect成功的listener之后执行,所以这个时候listener里的channel并不知道自己已经断开,它还是会继续进行write跟flush操作,在调用flush后,eventloop会进入OP_READ事件里,这时候unsafe.read()就会抛出connectionreset异常.eventloop代码如下
NioEventLoop
privatestaticvoidprocessSelectedKey(SelectionKeyk,AbstractNioChannelch){
finalNioUnsafeunsafe=ch.unsafe();
if(!
k.isValid()){
//closethechannelifthekeyisnotvalidanymore
unsafe.close(unsafe.voidPromise());
return;
}
try{
intreadyOps=k.readyOps();
//AlsocheckforreadOpsof0toworkaroundpossibleJDKbugwhichmayotherwiselead
//toaspinloop
if((readyOps&(SelectionKey.OP_READ|SelectionKey.OP_ACCEPT))!
=0||readyOps==0){
unsafe.read();
if(!
ch.isOpen()){
//Connectionalreadyclosed-noneedtohandlewrite.
return;
}
}
if((readyOps&SelectionKey.OP_WRITE)!
=0){
//CallforceFlushwhichwillalsotakecareofcleartheOP_WRITEoncethereisnothinglefttowrite
ch.unsafe().forceFlush();
}
if((readyOps&SelectionKey.OP_CONNECT)!
=0){
//removeOP_CONNECTasotherwiseSelector.select(..)willalwaysreturnwithoutblocking
//See
intops=k.interestOps();
ops&=~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
}catch(CancelledKeyExceptione){
unsafe.close(unsafe.voidPromise());
}
}
这就是connectionresetbypeer产生的原因
------------------
再来看ClosedChannelException如何产生,要复现他也很简单.首先要明确,并没有客户端主动关闭才会出现ClosedChannelException这么一说.下面来看两种出现ClosedChannelException的客户端写法
client1,主动关闭channel
publicclassSimpleClient{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SimpleClient.class);
publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{
Bootstrapb=newBootstrap();
b.group(newNioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(newChannelInitializer
@Override
protectedvoidinitChannel(NioSocketChannelch)throwsException{
}
});
b.connect("localhost",8090).addListener(newChannelFutureListener(){
@Override
publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(future.isSuccess()){
future.channel().close();
future.channel().write(Unpooled.buffer().writeBytes("123".getBytes())).addListener(newChannelFutureListener(){
@Override
publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(!
future.isSuccess()){
logger.error("Error",future.cause());
}
}
});
future.channel().flush();
}
}
});
}
}
只要在write之前主动调用了close,那么write必然会知道close是close状态,最后write就会失败,并且future里的cause就是ClosedChannelException
--------------------
client2.由服务端造成的ClosedChannelException
publicclassSimpleClient{
privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SimpleClient.class);
publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{
Bootstrapb=newBootstrap();
b.group(newNioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(newChannelInitializer
@Override
protectedvoidinitChannel(NioSocketChannelch)throwsException{
}
});
Channelchannel=b.connect("localhost",8090).sync().channel();
Thread.sleep(3000);
channel.writeAndFlush(Unpooled.buffer().writeBytes("123".getBytes())).addListener(newChannelFutureListener(){
@Override
publicvoidoperationComplete(ChannelFuturefuture)throwsException{
if(!
future.isSuccess()){
logger.error("error",future.cause());
}
}
});
}
}
服务端
publicclassSimpleServer{
publicstaticvoidmain(String[]args)throwsException{
EventLoopGroupbossGroup=newNioEventLoopGroup
(1);
EventLoopGroupworkerGroup=newNioEventLoopGroup();
ServerBootstrapb=newServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_REUSEADDR,true)
.childHandler(newChannelInitializer
@Override
protectedvoidinitChannel(NioSocketChannelch)throwsException{
ch.pipeline().addLast(newSimpleServerHandler());
}
});
b.bind(8090).sync().channel().closeFuture().sync();
}
}
这种情况下, 服务端将channel关闭,客户端先sleep,这期间client的eventLoop会处理客户端关闭的时间,也就是eventLoop的processKey方法会进入OP_READ,然后read出来一个-1,最后触发clientchannelInactive事件,当sleep醒来以后,客户端调用writeAndFlush,这时候客户端channel的状态已经变为了inactive,所以write失败,cause为ClosedChannelException
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Netty IOException Connection reset by peer javaniochannelsClosedChannelException null
链接地址:https://www.bingdoc.com/p-17647982.html