首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >缺失[鳍,ACK]和连接复位

缺失[鳍,ACK]和连接复位
EN

Stack Overflow用户
提问于 2014-11-13 22:24:41
回答 1查看 2.7K关注 0票数 0

我有两个问题,这两个问题都在下面解释了这个问题。

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

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

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

对于带有一个代理的配置,应用如下: client -> proxy 1-> HTTP服务器

两个代理服务器:客户机->代理1->代理2-> HTTP服务器

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

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

当配置了两个代理时,事情就结束了。HTTP服务器向代理2发送FIN,ACK。proxy 2通过使用FIN,ACK正确地关闭与HTTP服务器的连接。然后,代理2将FIN,ACK发送到代理1,代理1通过应答FIN、ACK正确地关闭到代理2的连接。然后,代理1将FIN、ACK发送到HTTP客户端。HTTP客户端使用ACK进行响应,但从不发送FIN。如果客户端在Linux或Mac上运行,就会发生这种情况。

我的第一个问题是,为什么客户端不发送关闭鳍,ACK到proxy1?我找不到任何挂起的写入或其他我可以找到的数据。

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

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

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

代码语言:javascript
复制
    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");

要写入连接请求:

代码语言:javascript
复制
    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)写入。

执行请求:

代码语言:javascript
复制
        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.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.SocketChannelImpl.read(java.nio.ByteBuffer) @bci=234 (编译帧)- com.....io.proxy.impl.PassThroughChannel.read(java.nio.ByteBuffer,java.nio.channels.ByteChannel) @bci=28,line=42 (编译帧)- com.....io.proxy.impl.HttpProxyIOSession$HttpProxyInternalByteChannel.read(java.nio.ByteBuffer) @bci=105,编译帧- 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,编译帧- 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,编译帧- org.apache.http.impl.nio.reactor.BaseIOReactor.readable(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,编译帧- 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 (解释帧)

EN

回答 1

Stack Overflow用户

发布于 2014-11-13 23:30:57

我的第一个问题是,为什么客户端不发送关闭鳍,ACK到proxy1?我找不到任何挂起的写入或其他我可以找到的数据。

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

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

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

经过。您确定客户端收到了RST

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26919616

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档