Java 缺少[FIN,ACK]和连接重置

Java 缺少[FIN,ACK]和连接重置,java,linux,apache,tcp,proxy,Java,Linux,Apache,Tcp,Proxy,我有两个问题,在解释完问题后都在下面 我正在尝试使用Apache的Java异步HTTP客户端库为HTTP请求开发代理链接支持。这主要是通过提供一些定制的SchemeOperationStrategy类来实现的。我发现[FIN,ACK]序列存在一些问题,配置多个代理时,丢失的连接会重置 如果没有配置代理,正在开发的代码将专门使用Apache库。如果配置了多个代理,则自定义SchemeOperationStrategy类将提供连接到代理的设置 要通过代理进行连接,代码将打开到第一个代理服务器的连接,

我有两个问题,在解释完问题后都在下面

我正在尝试使用Apache的Java异步HTTP客户端库为HTTP请求开发代理链接支持。这主要是通过提供一些定制的SchemeOperationStrategy类来实现的。我发现[FIN,ACK]序列存在一些问题,配置多个代理时,丢失的连接会重置

如果没有配置代理,正在开发的代码将专门使用Apache库。如果配置了多个代理,则自定义SchemeOperationStrategy类将提供连接到代理的设置

要通过代理进行连接,代码将打开到第一个代理服务器的连接,然后发出连接请求。如果代理服务器是链中的最后一个,它将连接到目标服务器,否则它将连接到链中的下一个代理服务器。完成所有设置后,将使用Apache库发送HTTP请求

对于具有一个代理的配置,以下内容适用: 客户端->代理1->HTTP服务器

使用两个代理服务器: 客户端->代理1->代理2->HTTP服务器

目标是尽可能重用连接。当转到特定HTTP服务器时,服务器会在空闲时间后关闭连接。如果使用相同的连接执行请求,则代码将经过重新连接过程

在所有场景中使用的所有HTTP请求都是相同的。Linux和Mac OS上的客户端都运行相同版本的Java 7。 当只配置一个代理时,一切正常。HTTP服务器向代理发送[FIN,ACK]。代理通过回复[FIN,ACK]来关闭与HTTP服务器的连接。然后,代理通过发送[FIN,ACK]来关闭与HTTP客户端的连接。客户端用[FIN,ACK]响应,代理用ACK响应以完成关闭

当配置两个代理时,一切都会顺利进行。HTTP服务器向代理2发送[FIN,ACK]。代理2通过响应[FIN,ACK]正确关闭与HTTP服务器的连接。然后,代理2向代理1发送[FIN,ACK],代理1通过回复[FIN,ACK]正确关闭与代理2的连接。然后,代理1向HTTP客户端发送[FIN,ACK]。HTTP客户端用ACK响应,但从不发送FIN。如果客户端在Linux或Mac OS上运行,则会发生这种情况

我的第一个问题是,为什么客户机不能将结束[FIN,ACK]发送给proxy1?我找不到任何挂起的写入或其他可以找到的数据

如果随后使用相同的连接尝试请求,则代理1将发送一个RST响应,指示该连接不可用。如果客户端在Mac OS上运行,则会导致客户端出现连接重置异常。在此之后,重新连接成功,请求得到处理

如果客户端在Linux上运行,则客户端永远不会收到连接重置异常,并且客户端挂起在Channel.read上。 我的第二个问题是,为什么Linux不向客户端显示连接重置异常

目前正在测试HTTP代理,与代理的连接如下:

    HttpRequest request = null;

    String uri = _proxyHost.getTargetHost().getHostName() + ":" + _proxyHost.getTargetHost().getPort();
    ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 0);
    request = new BasicHttpRequest("CONNECT", uri, protocolVersion);

    if ((_proxyHost.getPrincipal() != null) && !_proxyHost.getPrincipal().isEmpty()) {
        StringBuilder builder = new StringBuilder(_proxyHost.getPrincipal());

        if ((_proxyHost.getCredentials() != null) && !_proxyHost.getCredentials().isEmpty()) {
            builder.append(":");
            builder.append(_proxyHost.getCredentials());
        }

        String encodedAuthString = DatatypeConverter.printBase64Binary(builder.toString().getBytes("UTF-8"));
        request.addHeader("Proxy-Authorization", "Basic " + encodedAuthString);
    }

    request.addHeader("Pragma", "No-Cache");
    request.addHeader("Proxy-Connection", "Keep-Alive");
    request.addHeader("Connection", "Keep-Alive");
要写入连接请求,请执行以下操作:

    StringBuilder builder = new StringBuilder();
    RequestLine requestLine = request.getRequestLine();

    builder.append(requestLine.getMethod());
    builder.append(" ");
    builder.append(requestLine.getUri());
    builder.append(" ");
    builder.append(requestLine.getProtocolVersion().toString());
    builder.append(CRLF);

    for (Header header : request.getAllHeaders()) {
        builder.append(header.toString());
        builder.append(CRLF);
    }

    builder.append(CRLF);

    ByteBuffer byteBuffer = ByteBuffer.wrap(builder.toString().getBytes());
    ioSession.channel().write(byteBuffer);
使用ioSession.channel.read已准备好响应。ioSession是org.apache.http.nio.reactor.ioSession对象

由于它是HTTP代理,读取由ioSession.channel.readdst处理,写入由ioSession.channel.writesc处理

要执行请求,请执行以下操作:

        HttpAsyncClientBuilder builder = HttpAsyncClients.custom().setRedirectStrategy(new LaxRedirectStrategy());
        ConnectingIOReactor ioReactor = IOReactorFactory.getInstance().createConnectingReactor();
        Registry<SchemeIOSessionStrategy> registry = generateRegistry(config);

        builder.setConnectionManager(new PoolingNHttpClientConnectionManager(ioReactor, registry));

        if (config.getProxy() != null) {
            proxyHost = new ProxyHost(config.getProxy());
            builder.setProxy(proxyHost);
        }

        CloseableHttpAsyncClient client = builder.build();
        client.start();

    RequestConfig.Builder configBuilder = RequestConfig.custom();
    configBuilder.setRedirectsEnabled(true);
    requestBuilder.setConfig(configBuilder.build());

    client.execute(requestBuilder.build(), context, handler);
客户端因以下堆栈跟踪而挂起:

线程15996:state=IN_NATIVE -sun.nio.ch.FileDispatcherImpl.read0java.io.FileDescriptor,长,int@bci=0编译帧;信息可能不准确 -sun.nio.ch.SocketDispatcher.readjava.io.FileDescriptor,长,int@bci=4编译帧 -sun.nio.ch.IOUtil.readIntoNativeBufferjava.io.FileDescriptor,java.nio.ByteBuffer,long,sun.nio.ch.NativeDispatcher@bci=114编译帧 -sun.nio.ch.IOUtil.readjava.io.FileDescriptor,java.nio.ByteBuffer,long,sun.nio.ch.NativeDispatcher@bci=48编译帧 -sun.nio.ch.SocketChannelImpl.readjava.nio.ByteBuffer@bci=234编译帧 -com..io.proxy.impl.PassThroughChannel.readjava.nio.ByteBuffer,java.nio.channels.ByteChannel@bci=28,line=42编译帧 -com..io.proxy.impl.HttpProxyIOSession$HttpProxyInternalByteChannel.readjava.nio.ByteBuffer@bci=105,行=220编译帧 -org.apache.http.impl.nio.reactor.SessionInputBufferImpl.filljava.nio.channels.ReadableByteChannel@bci=30,行=164编译帧 -org.apache.http.impl.nio.codecs.AbstractMessageParser.fillBufferjava.nio.channels.ReadableByteChannel@bci=5,line=136编译帧 -org.apache.http.impl.nio.DefaultNHttpClientConnection.consumerinputorg.apache.http.nio.NHttpClientEventHandler@bci=38,行=241编译帧 -org.apache.http.impl.nio.client.internalioidispatch.onInputReadyorg.apache.http.impl.nio.DefaultNHttpClientConnection@bci=5,第73行编译帧 -org.ap ache.http.impl.nio.client.internalioidispatch.onInputReadyjava.lang.Object@bci=5,行=37编译帧 -org.apache.http.impl.nio.reactor.abstractioidispatch.inputReadyorg.apache.http.nio.reactor.IOSession@bci=32,第113行编译帧 -org.apache.http.impl.nio.reactor.BaseIOReactor.readablejava.nio.channels.SelectionKey@bci=11,line=159编译帧 -org.apache.http.impl.nio.reactor.AbstractIOReactor.processEventjava.nio.channels.SelectionKey@bci=45,line=338编译帧 -org.apache.http.impl.nio.reactor.AbstractIOReactor.processEventsjava.util.Set@bci=28,第316行编译帧 -org.apache.http.impl.nio.reactor.AbstractIOReactor.execute@bci=80,line=277编译帧 -org.apache.http.impl.nio.reactor.BaseIOReactor.executeorg.apache.http.nio.reactor.IOEventDispatch@bci=13,第105行 -org.apache.http.impl.nio.reactor.AbstractMultiWorkPerioreactor$Worker.run@bci=8,line=584解释帧 -java.lang.Thread.run@bci=11解释帧

我的第一个问题是,为什么客户机不能将结束[FIN,ACK]发送给proxy1?我找不到任何挂起的写入或其他可以找到的数据

如果客户端已从对等方接收到FIN,但尚未发送FIN,则客户端端口将处于关闭等待状态,等待客户端本地应用程序关闭其套接字。客户端可能仍在尝试连接池,并将在下次使用该连接时发现关闭

链中不同数量的代理为什么会有不同,这是一个谜,除非它以某种方式影响了连接:header。也许Apache库会发送一个

如果客户端在Linux上运行,则客户端永远不会收到连接重置异常,并且客户端挂起在Channel.read上。我的第二个问题是,为什么Linux不向客户端显示连接重置异常


通过。您确定客户收到了RST吗?

即使您有非常详细的问题描述,请包括一些代码,特别是在您怀疑第一个问题存在问题的区域。如果没有你的代码,我看不出我们如何回答第二个问题。我尝试过使用和不使用连接头,结果都是一样的。是的,客户端得到RST。但是,我没有看到从客户端获取的Wireshark对RST的确认:339 28.494821 192.168.72.161->192.168.71.222 TCP 60 3128→56387[RST]序列=256 Win=0 Len=0