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

标签 java linux apache tcp proxy

我有两个问题,下面都是问题的解释。

我正在尝试使用Apache的Java异步HTTP客户端库为HTTP请求开发代理链接支持。这主要是通过提供一些自定义的SchemeIOSessionStrategy类来完成的。我看到[FIN,ACK]序列存在一些问题,并且在配置多个代理时缺少连接重置。

如果未配置代理,则正在开发的代码将仅使用Apache库。如果配置了多个代理,则自定义SchemeIOSessionStrategy类将提供用于连接到代理的设置。

要通过代理进行连接,代码将打开与第一个代理服务器的连接,然后发出CONNECT请求。如果代理服务器是链中的最后一个,它将连接到目标服务器,否则将连接到链中的下一个代理服务器。完成所有设置后,将使用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服务器将[FIN,ACK]发送到代理2。代理2通过响应[FIN,ACK]正确关闭与HTTP服务器的连接。然后,代理服务器2将[FIN,ACK]发送到代理服务器1,代理服务器2通过用[FIN,ACK]答复正确关闭与代理服务器2的连接。然后,代理1将[FIN,ACK]发送到HTTP客户端。 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()。read(dst)处理,写入操作则由ioSession.channel()。write(src)处理。

要执行请求:

        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:(状态= IN_NATIVE)
 -sun.nio.ch.FileDispatcherImpl.read0(java.io.FileDescriptor,long,int)@ bci = 0(编译帧;信息可能不准确)
 -sun.nio.ch.SocketDispatcher.read(java.io.FileDescriptor,long,int)@ bci = 4(编译的帧)
 -sun.nio.ch.IOUtil.readIntoNativeBuffer(java.io.FileDescriptor,java.nio.ByteBuffer,long,sun.nio.ch.NativeDispatcher)@ bci = 114(编译帧)
 -sun.nio.ch.IOUtil.read(java.io.FileDescriptor,java.nio.ByteBuffer,long,sun.nio.ch.NativeDispatcher)@ bci = 48(编译帧)
 -sun.nio.ch.SocketChannelImpl.read(java.nio.ByteBuffer)@ bci = 234(编译的帧)
 -com ..... io.proxy.impl.PassThroughChannel.read(java.nio.ByteBuffer,java.nio.channels.ByteChannel)@ bci = 28,线= 42(编译帧)
 -com ..... io.proxy.impl.HttpProxyIOSession $ HttpProxyInternalByteChannel.read(java.nio.ByteBuffer)@ bci = 105,line = 220(编译帧)
 -org.apache.http.impl.nio.reactor.SessionInputBufferImpl.fill(java.nio.channels.ReadableByteChannel)@ bci = 30,line = 164(编译帧)
 -org.apache.http.impl.nio.codecs.AbstractMessageParser.fillBuffer(java.nio.channels.ReadableByteChannel)@ bci = 5,line = 136(编译帧)
 -org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(org.apache.http.nio.NHttpClientEventHandler)@ bci = 38,line = 241(编译框架)
 -org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(org.apache.http.impl.nio.DefaultNHttpClientConnection)@ bci = 5,line = 73(编译框架)
 -org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(java.lang.Object)@ bci = 5,line = 37(编译框架)
 -org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(org.apache.http.nio.reactor.IOSession)@ bci = 32,line = 113(编译框架)
 -org.apache.http.impl.nio.reactor.BaseIOReactor。可读(java.nio.channels.SelectionKey)@ bci = 11,line = 159(编译帧)
 -org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(java.nio.channels.SelectionKey)@ bci = 45,line = 338(编译帧)
 -org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(java.util.Set)@ bci = 28,line = 316(编译帧)
 -org.apache.http.impl.nio.reactor.AbstractIOReactor.execute()@ bci = 80,line = 277(编译帧)
 -org.apache.http.impl.nio.reactor.BaseIOReactor.execute(org.apache.http.nio.reactor.IOEventDispatch)@ bci = 13,line = 105(解释帧)
 -org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor $ Worker.run()@ bci = 8,line = 584(解释帧)
 -java.lang.Thread.run()@ bci = 11(解释帧)

最佳答案

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


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

为什么这与链中不同数量的代理不同的原因是一个谜,除非它以某种方式影响RST标头。也许Apache库发送了一个?


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


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

关于java - 缺少[FIN,ACK]和连接重置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26919616/

相关文章:

Java - Swing 表单在被调用时显示为空

audio - 较新版本的 JRE 不加载音库,但旧版本会加载

linux - 如何访问一些邮件,并打印特定邮件的邮件正文?

Java:不支持的内容编码:text/xml posting text/xml

php - 在 Linux 中启用 apache 服务器的错误报告

java - 为什么 Lucene 结果只包含完全匹配?

java - 使用Scanner读取文件Java时无限循环

linux - 在 JBoss 中连接到 gmail imap 时,MessagingException 嵌套 IOException

linux - 如何在linux中用新行写入文件结尾?

Python:numpy:检测到解释器更改 - 每个进程只能将此模块加载到一个解释器中