当客户端被物理杀死时,总是无法从服务器端关闭通道(netty)

Posted

技术标签:

【中文标题】当客户端被物理杀死时,总是无法从服务器端关闭通道(netty)【英文标题】:Always failed to close channel (netty) from server-side when the client has been physically killed 【发布时间】:2020-05-29 20:53:27 【问题描述】:

这是一个基于 netty 的 web-socket 应用。但是,当客户端在没有发送CloseWebSocketFrame 的情况下被物理杀死时,服务器需要知道通道已关闭并进行一些清理工作。 IdleStateHandler用于监控通道是否空闲。

这是通道处理程序管道。

public void initChannel(SocketChannel ch) throws Exception 
    ChannelPipeline pipeline = ch.pipeline();
    SSLEngine sslEngine = new SSLServiceBase().getSSLContext().createSSLEngine();
    sslEngine.setUseClientMode(false);
    sslEngine.setNeedClientAuth(true);
    ... // business process
    pipeline.addLast(new IdleStateHandler(30, 10, 0, TimeUnit.SECONDS));
    pipeline.addLast(new HeartbeatHandler());

这是HeartbeatHandleruserEventTriggered方法,用于在客户端异常关闭通道时做一些清理工作。

public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception 
    if (evt instanceof IdleStateEvent) 
        IdleStateEvent event = (IdleStateEvent) evt;
        if (IdleState.WRITER_IDLE.equals(event.state())) 
            ctx.writeAndFlush(new PingWebSocketFrame()).addListener(future -> 
                if (!future.isSuccess()) 
                    ChannelFuture closeFuture = ctx.channel().close();
                    if (closeFuture.isSuccess()) 
                        System.out.println("ping faild, channel close successfully");
                     else 
                        System.out.println("ping failed, channel close failed");
                    
                 else 
                    System.out.println("Ping succeed, keep the channel.");
                
            );
        
     else 
        super.userEventTriggered(ctx, evt);
    

实际上,我不断收到'close failed',并且从服务器视图来看,该频道仍处于活动状态。谁能告诉我为什么无法关闭频道或如何关闭?非常感谢。

【问题讨论】:

【参考方案1】:

我怀疑关闭还没有完成(记住这是一个异步操作)。将代码更改为:

public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception 
    if (evt instanceof IdleStateEvent) 
        IdleStateEvent event = (IdleStateEvent) evt;
        if (IdleState.WRITER_IDLE.equals(event.state())) 
            ctx.writeAndFlush(new PingWebSocketFrame()).addListener(future ->  
                if (!future.isSuccess()) 
                    ctx.close().addListener(closeFuture -> 
                        If (closeFuture.isSuccess()) 
                            System.out.println("ping faild, channel close successfully");
                         else 
                            System.out.println("ping failed, channel close failed");
                            // TODO: You may also want to log the reason why the close operation failed. 
                            //       closeFuture.cause() will contain why.
                        
                    );                    
                 else 
                    System.out.println("Ping succeed, keep the channel.");
                
        );
    
 else 
    super.userEventTriggered(ctx, evt);

【讨论】:

我阅读了ChannelFuture 的大多数方法,但我错过了cause()。实际上在ChannelHandler 之一的方法void close(ChannelHandlerContext ctx, ChannelPromise promise) 中发生了异常,因此通道无法在不打印消息的情况下关闭。这是正确的答案。

以上是关于当客户端被物理杀死时,总是无法从服务器端关闭通道(netty)的主要内容,如果未能解决你的问题,请参考以下文章

当客户端关闭连接时,Cowboy/Ranch 杀死处理程序进程

NIO 网络通信

当 kivy 应用程序被杀死时关闭数据库连接

是否可以在flutter应用程序关闭时通过平台通道从android后台服务向flutter端发送消息

当应用程序从堆栈中被杀死时,后台服务停止

当应用程序被杀死/关闭时,世博会通知不会触发方法