在解释问题后,我有两个问题。
我正在尝试使用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,代理1通过回复[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 :( 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.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,line = 42(Compiled frame) - com ..... io.proxy.impl.HttpProxyIOSession $ HttpProxyInternalByteChannel.read(java.nio.ByteBuffer)@ bci = 105,line = 220(Compiled frame) - 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.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,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(解释框架)
答案 0 :(得分:0)
我的第一个问题是为什么客户端不能将结束[FIN,ACK]发送给proxy1?我无法找到任何可以找到的待处理的写入或其他数据。
如果客户端已从对等方收到FIN
并且未发送一个,则客户端端口将处于CLOSE-WAIT
状态,等待客户端本地应用程序关闭其插座。客户端可能仍在尝试连接池,并将在下次使用该连接时发现关闭。
为什么这与链中不同数量的代理不同是一个谜,除非它以某种方式影响Connection:
标题。也许Apache库会发送一个?
如果客户端在Linux上运行,则客户端永远不会获得连接重置异常,并且客户端会挂起Channel.read()。我的第二个问题是Linux为什么不向客户端提供连接重置异常。
传递。您确定客户收到了RST
吗?