java - 使用 SSL 时 Netty ChannelHandler 关闭

标签 java ssl client netty keep-alive

我在客户端/服务器环境中使用 netty 时遇到问题。设置服务器后

bossGroup = new NioEventLoopGroup(); 
workerGroup = new NioEventLoopGroup(); 
try {
    serverBootstrap = new ServerBootstrap(); 
    serverBootstrap.group(bossGroup, workerGroup); 
    serverBootstrap.channel(NioServerSocketChannel.class); 
    serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { 
        @Override
        protected void initChannel(SocketChannel channel) throws Exception {
            channel.pipeline().addLast(new ObjectEncoder(), new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)), new UserPacketDecoder(serverRev));
            channel.pipeline().addLast(new LoggingHandler());
        }
    });
    logger.info(language.getProperty("Server.Info.Netty.PortOpened"));
    serverBootstrap.bind(serverPort).sync().channel().closeFuture().sync().channel().closeFuture().sync();      
} catch (Exception e) {
    logger.error(language.getProperty("Server.Error.Netty.Initialize"), e);
}   

使用 ChannelInboundHandlerAdapter“UserPacketDecoder”:

@Override
public void channelRead(ChannelHandlerContext ctx, Object incomingPacket) {
    /* Identify */   
    if(incomingPacket instanceof GetNews){
        logger.info("Incoming -> GetNews");
        GetNewsAnswer outgoingPacket = new GetNewsAnswer();
        ctx.writeAndFlush(outgoingPacket).addListener(ChannelFutureListener.CLOSE);
    } 
}


@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
    logger.error("Error in Netty-Core", e);
    ctx.close();
}

客户端连接,发送请求数据包,等待来自服务器的应答数据包,应该通过以下方式关闭连接:

workerGroup = new NioEventLoopGroup();
bootstrap = new Bootstrap(); 
bootstrap.group(workerGroup); 
bootstrap.channel(NioSocketChannel.class); 
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
    @Override
    public void initChannel(SocketChannel channel)  {                       
        channel.pipeline().addLast(new ObjectEncoder(), new ObjectDecoder(50000000, ClassResolvers.cacheDisabled(null)), new PacketRelay());
        channel.pipeline().addLast(new LoggingHandler());
    }
});  
try {    
    bootstrap.connect(hostIP, hostPort).sync().channel().closeFuture().sync(); 
} catch (Exception e) { 
    logger.error("Error connecting", e);
}   finally {
    workerGroup.shutdownGracefully();
}  

使用 ChannelInboundHandlerAdapter“PacketRelay”:

@Override
public void channelActive(ChannelHandlerContext ctx) {
    if( ! (outgoingPacket == null)){
        ctx.writeAndFlush(outgoingPacket);
    }
}       
@Override
public void channelRead(ChannelHandlerContext ctx, Object incomingPacket) {
    incomingPacketQueue.add(incomingPacket);
    ctx.close();
}   

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
    logger.error("Error in Netty-Core", e);
    ctx.close();
}

没有加密一切正常! 现在我在服务器端启用 SSL(自签名证书)

System.setProperty("javax.net.ssl.keyStore", ksPath);
System.setProperty("javax.net.ssl.keyStorePassword", ksPW);
System.setProperty("javax.net.ssl.trustStore", tsPath);
System.setProperty("javax.net.ssl.trustStorePassword", tsPW);

SSLContext ctx = null;
try {
    ctx = SSLContext.getDefault();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}   

final SSLEngine engine = ctx.createSSLEngine();
engine.setUseClientMode(false);
engine.setNeedClientAuth(false); //tried both -> true and false

channel.pipeline().addLast("sslHandler", new SslHandler(engine));

作为管道中的第一个元素。在客户端非常相似

SSLContext ctx = null;
try {
    ctx = SSLContext.getDefault();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
}   

final SSLEngine engine = ctx.createSSLEngine();
engine.setUseClientMode(true);

channel.pipeline().addLast("sslHandler", new SslHandler(engine));

作为管道中的第一个元素。如您所见,我通过向管道添加新的 LoggingHandler() 启用了 netty 的 Debug模式。我也设置了

InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory());
System.setProperty("javax.net.debug", "ssl"); 
System.setProperty("ssl.debug", "true");

用于额外的日志记录。

来自客户端的第一个请求按预期处理,但我在客户端看到异常:

nioEventLoopGroup-2-1, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data:  { 30, 112, 54, 24, 223, 62, 135, 143, 88, 178, 186, 12 }
***
nioEventLoopGroup-2-1, WRITE: TLSv1.2 Handshake, length = 96
nioEventLoopGroup-2-1, READ: TLSv1.2 Change Cipher Spec, length = 1
nioEventLoopGroup-2-1, READ: TLSv1.2 Handshake, length = 96
*** Finished
verify_data:  { 5, 115, 178, 143, 196, 203, 86, 123, 42, 40, 44, 34 }
***
%% Cached client session: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
2018-02-19 16:45:36 DEBUG SslHandler:1435 - [id: 0x0c2c19e6, L:/127.0.0.1:54629 - R:localhost/127.0.0.1:23600] HANDSHAKEN: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x0c2c19e6, L:/127.0.0.1:54629 - R:localhost/127.0.0.1:23600] USER_EVENT: SslHandshakeCompletionEvent(SUCCESS)
nioEventLoopGroup-2-1, WRITE: TLSv1.2 Application Data, length = 129
nioEventLoopGroup-2-1, called closeOutbound()
nioEventLoopGroup-2-1, closeOutboundInternal()
nioEventLoopGroup-2-1, SEND TLSv1.2 ALERT:  warning, description = close_notify
nioEventLoopGroup-2-1, WRITE: TLSv1.2 Alert, length = 80
nioEventLoopGroup-2-1, called closeOutbound()
nioEventLoopGroup-2-1, closeOutboundInternal()
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x0c2c19e6, L:/127.0.0.1:54629 ! R:localhost/127.0.0.1:23600] USER_EVENT: SslCloseCompletionEvent(java.nio.channels.ClosedChannelException)
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x0c2c19e6, L:/127.0.0.1:54629 ! R:localhost/127.0.0.1:23600] INACTIVE
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x0c2c19e6, L:/127.0.0.1:54629 ! R:localhost/127.0.0.1:23600] UNREGISTERED
GetNews OK   //<-------------------- Answer from server
2018-02-19 16:45:39 DEBUG PoolThreadCache:262 - Freed 13 thread-local buffer(s) from thread: nioEventLoopGroup-2-1

在服务器端

nioEventLoopGroup-3-1, READ: TLSv1.2 Change Cipher Spec, length = 1
nioEventLoopGroup-3-1, READ: TLSv1.2 Handshake, length = 96
*** Finished
verify_data:  { 30, 112, 54, 24, 223, 62, 135, 143, 88, 178, 186, 12 }
***
nioEventLoopGroup-3-1, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data:  { 5, 115, 178, 143, 196, 203, 86, 123, 42, 40, 44, 34 }
***
nioEventLoopGroup-3-1, WRITE: TLSv1.2 Handshake, length = 96
%% Cached server session: [Session-2, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
2018-02-19 16:45:36 DEBUG SslHandler:1435 - [id: 0x6245c2c5, L:/127.0.0.1:23600 - R:/127.0.0.1:54629] HANDSHAKEN: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x6245c2c5, L:/127.0.0.1:23600 - R:/127.0.0.1:54629] USER_EVENT: SslHandshakeCompletionEvent(SUCCESS)
2018-02-19 16:45:36 INFO  UserPacketDecoder:865 - IncomingPacket -> GetNews        //<-------------------- Request from client
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 144
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 112
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 144
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 112
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 176
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 160
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 96
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 992
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 96
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 1136
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 112
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 432
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 80
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 64
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 96
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 64
nioEventLoopGroup-3-1, WRITE: TLSv1.1 Application Data, length = 80
nioEventLoopGroup-3-1, READ: TLSv1.1 Application Data, length = 128
nioEventLoopGroup-3-1, WRITE: TLSv1.2 Application Data, length = 122
nioEventLoopGroup-3-1, READ: TLSv1.2 Alert, length = 80
nioEventLoopGroup-3-1, RECV TLSv1.2 ALERT:  warning, close_notify
nioEventLoopGroup-3-1, closeInboundInternal()
nioEventLoopGroup-3-1, closeOutboundInternal()
nioEventLoopGroup-3-1, SEND TLSv1.2 ALERT:  warning, description = close_notify
nioEventLoopGroup-3-1, WRITE: TLSv1.2 Alert, length = 80
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x6245c2c5, L:/127.0.0.1:23600 - R:/127.0.0.1:54629] USER_EVENT: SslCloseCompletionEvent(SUCCESS)
nioEventLoopGroup-3-1, called closeOutbound()
nioEventLoopGroup-3-1, closeOutboundInternal()
nioEventLoopGroup-3-1, called closeInbound()
nioEventLoopGroup-3-1, closeInboundInternal()
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x6245c2c5, L:/127.0.0.1:23600 ! R:/127.0.0.1:54629] INACTIVE
2018-02-19 16:45:36 DEBUG LoggingHandler:147 - [id: 0x6245c2c5, L:/127.0.0.1:23600 ! R:/127.0.0.1:54629] UNREGISTERED

从现在开始,客户端的每次连接尝试都由服务器上的 SSLHandler 注册,但不会传输任何对象。客户端输出:

nioEventLoopGroup-2-1, WRITE: TLSv1.2 Handshake, length = 203
2018-02-19 16:55:39 DEBUG Recycler:96 - -Dio.netty.recycler.maxCapacityPerThread: 32768
2018-02-19 16:55:39 DEBUG Recycler:97 - -Dio.netty.recycler.maxSharedCapacityFactor: 2
2018-02-19 16:55:39 DEBUG Recycler:98 - -Dio.netty.recycler.linkCapacity: 16
2018-02-19 16:55:39 DEBUG Recycler:99 - -Dio.netty.recycler.ratio: 8
2018-02-19 16:55:39 DEBUG AbstractByteBuf:52 - -Dio.netty.buffer.bytebuf.checkAccessible: true
2018-02-19 16:55:39 DEBUG ResourceLeakDetectorFactory:202 - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@13e73780
2018-02-19 16:55:49 DEBUG LoggingHandler:147 - [id: 0x25b3173b, L:/127.0.0.1:55853 - R:localhost/127.0.0.1:23600] USER_EVENT: SslHandshakeCompletionEvent(javax.net.ssl.SSLException: handshake timed out)
nioEventLoopGroup-2-1, called closeOutbound()
nioEventLoopGroup-2-1, closeOutboundInternal()
nioEventLoopGroup-2-1, SEND TLSv1.2 ALERT:  warning, description = close_notify
nioEventLoopGroup-2-1, WRITE: TLSv1.2 Alert, length = 2
nioEventLoopGroup-2-1, called closeInbound()
nioEventLoopGroup-2-1, fatal error: 80: Inbound closed before receiving peer's close_notify: possible truncation attack?
javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack?
nioEventLoopGroup-2-1, SEND TLSv1.2 ALERT:  fatal, description = internal_error
nioEventLoopGroup-2-1, Exception sending alert: java.io.IOException: writer side was already closed.
2018-02-19 16:55:49 DEBUG LoggingHandler:147 - [id: 0x25b3173b, L:/127.0.0.1:55853 ! R:localhost/127.0.0.1:23600] USER_EVENT: SslCloseCompletionEvent(java.nio.channels.ClosedChannelException)
2018-02-19 16:55:49 DEBUG LoggingHandler:147 - [id: 0x25b3173b, L:/127.0.0.1:55853 ! R:localhost/127.0.0.1:23600] INACTIVE
2018-02-19 16:55:49 DEBUG LoggingHandler:147 - [id: 0x25b3173b, L:/127.0.0.1:55853 ! R:localhost/127.0.0.1:23600] UNREGISTERED
2018-02-19 16:55:51 DEBUG PoolThreadCache:262 - Freed 4 thread-local buffer(s) from thread: nioEventLoopGroup-2-1

我找不到答案...谢谢指教

最佳答案

问题是connection reuse当客户端应该正确关闭,并打开一个新的。有 2 个证据:它仅适用于第一个请求,以及 close_notify。客户端发送的消息(之后是服务器发送的消息)。在第一个成功的事务之后,日志显示 SSL 握手超时,这是应该已经关闭的 SSL 连接上的正常现象。

我现在对 netty 的了解还不够,无法给出确切的解决方案,但了解原因应该会对你有很大帮助。在这个thread他们提供了一种禁用 HTTP Keep-Alive 的方法,这正是我们想要实现的,方法是将 header 设置为关闭值。他们正在与相反的问题作斗争,但也许会为我们提供解决方案。

关于java - 使用 SSL 时 Netty ChannelHandler 关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48869769/

相关文章:

java - JSON 有模式吗?

java - 如何使用 Archiva 配置 SSL?

node.js - 如何创建一个同时监听两个不同端口的 sails.js 应用程序

java - Spring Autowiring 的bean导致空指针

java - 使用 StepVerifier 对 Flux.take(Duration duration) 进行单元测试

android - Android JSON 中的 SessionExpiredException 错误

java - 服务器-客户端这里出了什么问题?

java - 为什么这个 Java UDP 数据包长度太长?

java - 即使对于不同的 grpc 请求,方法也会保持运行并发送相同的信息

java - 是否有可以在 JRE 6 上使用 Java 5 编译器但不能使用 Java 6 编译器编译的 Java 程序片段?