Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Apache InternalHttpClient:确定本地端口号_Java_Apache Httpclient 4.x - Fatal编程技术网

Java Apache InternalHttpClient:确定本地端口号

Java Apache InternalHttpClient:确定本地端口号,java,apache-httpclient-4.x,Java,Apache Httpclient 4.x,我正在尝试调试http客户端请求。我需要获取连接时使用的本地端口号。我可以使用HttpClientContext获取连接,并在连接成功时检索本地端口。但是,在抛出IOException的情况下,在检索本地端口号时,我会得到ConnectionShutdownException。关于如何在出现错误时获取所有http请求的本地端口号的任何线索。这是针对HTTPClient 4.0.1(我随身携带的最新版本)。 我没有找到任何简单的一行 毫不奇怪,http客户端将套接字绑定到本地主机/端口并连接到远程

我正在尝试调试http客户端请求。我需要获取连接时使用的本地端口号。我可以使用HttpClientContext获取连接,并在连接成功时检索本地端口。但是,在抛出IOException的情况下,在检索本地端口号时,我会得到ConnectionShutdownException。关于如何在出现错误时获取所有http请求的本地端口号的任何线索。

这是针对HTTPClient 4.0.1(我随身携带的最新版本)。 我没有找到任何简单的一行

毫不奇怪,http客户端将套接字绑定到本地主机/端口并连接到远程主机/端口的部分是SocketFactory。在HTTPClient中,套接字工厂与SchemeRegistry关联,SchemeRegistry又属于连接管理器。请注意,HTTPClient的SocketFactory不是javax.net.SocketFactory,而是此类对象的包装器。您可以这样定义一个方案:

SchemeRegistry schRgstr = new SchemeRegistry();
Scheme httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
schRgstr.register(httpScheme);
当然,org.apache.http.conn.scheme.SocketFactory是一个接口,因此您可以对其进行修饰,以实现任何您想要的功能,特别是,这将派上用场

httpclient调用套接字工厂的部分称为
ClientConnectionOperator
(它也是一个接口)。这个对象实际上也绑定到连接管理器,而不是客户端本身。因此,如果要自定义连接运算符,也可以重写连接管理器,例如,类似这样(匿名类):

套接字的生命周期模型如下所示:

  • 当连接管理器需要now连接时,它会调用操作符上的
    createConnection
    (基本上什么都不做,只创建一个最终将容纳实际套接字的内部对象)
  • 沿着调用
    openConnection
  • openConnection转到
    SocketFactory
    并请求一个新的套接字,然后像这样尝试连接它(这里,sf是“httpclient套接字工厂”)

  • 如果此调用失败,将引发异常,无法访问更多信息。我们会继续讨论的

  • 但是,如果connectSocket有效,则会对连接操作符调用
    prepareSocket
    方法。因此,您可以重写该方法并将端口信息放入上下文中(或您喜欢的任何其他内容):

  • 调用HTTPClient时会传递所使用的HttpContext实例,因此即使稍后由于其他异常调用失败,也可以访问该实例。打电话时,请确保:

    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();