Java Apache InternalHttpClient:确定本地端口号
我正在尝试调试http客户端请求。我需要获取连接时使用的本地端口号。我可以使用HttpClientContext获取连接,并在连接成功时检索本地端口。但是,在抛出IOException的情况下,在检索本地端口号时,我会得到ConnectionShutdownException。关于如何在出现错误时获取所有http请求的本地端口号的任何线索。这是针对HTTPClient 4.0.1(我随身携带的最新版本)。 我没有找到任何简单的一行 毫不奇怪,http客户端将套接字绑定到本地主机/端口并连接到远程主机/端口的部分是SocketFactory。在HTTPClient中,套接字工厂与SchemeRegistry关联,SchemeRegistry又属于连接管理器。请注意,HTTPClient的SocketFactory不是javax.net.SocketFactory,而是此类对象的包装器。您可以这样定义一个方案:Java Apache InternalHttpClient:确定本地端口号,java,apache-httpclient-4.x,Java,Apache Httpclient 4.x,我正在尝试调试http客户端请求。我需要获取连接时使用的本地端口号。我可以使用HttpClientContext获取连接,并在连接成功时检索本地端口。但是,在抛出IOException的情况下,在检索本地端口号时,我会得到ConnectionShutdownException。关于如何在出现错误时获取所有http请求的本地端口号的任何线索。这是针对HTTPClient 4.0.1(我随身携带的最新版本)。 我没有找到任何简单的一行 毫不奇怪,http客户端将套接字绑定到本地主机/端口并连接到远程
SchemeRegistry schRgstr = new SchemeRegistry();
Scheme httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
schRgstr.register(httpScheme);
当然,org.apache.http.conn.scheme.SocketFactory是一个接口,因此您可以对其进行修饰,以实现任何您想要的功能,特别是,这将派上用场
httpclient调用套接字工厂的部分称为ClientConnectionOperator
(它也是一个接口)。这个对象实际上也绑定到连接管理器,而不是客户端本身。因此,如果要自定义连接运算符,也可以重写连接管理器,例如,类似这样(匿名类):
套接字的生命周期模型如下所示:
createConnection
(基本上什么都不做,只创建一个最终将容纳实际套接字的内部对象)openConnection
SocketFactory
并请求一个新的套接字,然后像这样尝试连接它(这里,sf是“httpclient套接字工厂”)
prepareSocket
方法。因此,您可以重写该方法并将端口信息放入上下文中(或您喜欢的任何其他内容):
HttpContext ctx = new BasicHttpContext();
HttpGet get = new HttpGet(targetUrl.getUrl());
HttpResponse resp = client.execute(get, ctx);
在这段代码中,如果客户端可以执行步骤5,那么即使稍后发生异常(连接断开、超时、无效HTTP等),也可以访问端口信息
更进一步
在所有这一切中仍然存在一个暗区:如果调用在步骤4失败(实际打开套接字,比如在解析目标主机名时出现DNS错误)。。。我不确定你对这个案子是否感兴趣,(我不明白为什么会这样)。看到处理它开始变得“凌乱”,你真的应该考虑你是否需要这个。
因为我们需要开始真正的压倒一切,这涉及到大量的工作,其中有些我认为不是很好的设计。
之所以会出现这种复杂性,是因为HTTPClient的作者没有提供必要的方法来覆盖以获取所需的信息。在插座工厂内部,有趣的一点是:
sock = createSocket();
InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);
sock.bind(isa);
// go on and connect to the server
// like socket.connect...
没有可重写的方法将套接字打开的本地和服务器端分开,因此,如果在服务器端引发任何套接字异常,您对套接字实例的访问将丢失,并且本地端口信息也随之丢失
但我们并没有失去一切,因为我们有一个可以使用的入口点:createSocket方法!默认实现是
public Socket createSocket() {
return new Socket();
}
但由于Socket不是最终类,您可以。。。玩吧
public Socket createSocket() {
return new Socket() {
@Override
public void bind(SocketAddress bindpoint) throws IOException {
super.bind(bindpoint);
// get the local port and give the info back to whomever you like
}
};
}
问题是:这适用于普通套接字(因为我们可以创建匿名子类),但这不适用于HTTPS,因为在这种情况下,您不能简单地实例化套接字,您必须执行以下操作:
return (SSLSocket) this.javaxNetSSLSocketFactory.createSocket();
在这种情况下,您不能创建匿名子类。由于套接字也不是接口,所以您甚至不能代理它来装饰它。因此,您可以(有史以来最难看的代码)创建一个套接字子类来包装并委托给SSLSocket,但这是非常困难的
那么,回顾一下时间。
如果我们只关心在某个点连接到服务器的套接字,那么解决方案就相当简单
我们构建的方案注册表用于覆盖连接运算符的自定义连接管理器
。操作员的重写方法是prepareSocket
,它允许我们简单地用端口信息更新发送的任何请求的HttpContext
。这是在一切顺利时查找信息的一种简单方法
如果我们想关心一个从未连接过的套接字的本地端口(我认为这是一个有限的用途),我们需要深入研究
我们自己的SchemeRegistry
应该设计为拥有定制的SocketFactories
。这些应该是装饰器或默认的装饰器。。。createSocket
重写方法允许我们“截获”本地端口上的绑定(并将其存储到ConcurrentMap
或ThreadLocal
中),通过重写“connectSocket”,我们捕获可能发生的任何异常,并重试它,但在将其包装到我们自己的异常类型(可以保存本地端口信息)之前,不需要这样做。这样,如果在调用客户机时异常通过,通过检查原因链,您将找到端口数据。如果没有异常发生,我们将清理或映射实例/线程本地。A连接
public Socket createSocket() {
return new Socket();
}
public Socket createSocket() {
return new Socket() {
@Override
public void bind(SocketAddress bindpoint) throws IOException {
super.bind(bindpoint);
// get the local port and give the info back to whomever you like
}
};
}
return (SSLSocket) this.javaxNetSSLSocketFactory.createSocket();